卧薪尝胆,厚积薄发。
模板 后缀自动机
Date: Wed Aug 15 22:20:54 CST 2018 In Category: NoCategory

Description:

给定一个只包含小写字母的字符串 $S$ ,求出 $S$ 的所有出现次数不为 $1$ 的子串的出现次数乘上该子串长度的最大值。
$1\le n \le 10^6$

Solution:

对 $S$ 建出 $SAM$ ,当新建一个 $np$ 节点时 $siz[np]++$ ,最后在 $parent$ 树上 $dfs$ 求和,由于 $parent$ 树的父亲是每个儿子的后缀,所以儿子的出现次数应累加到父亲上,由于 $nq$ 是后缀状态的转移,所以并不会产生新子串。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MAXL 1000010
char c[MAXL];
struct node
{
int tr[26],par,maxl;
}s[MAXL << 1];
int root,last,ptr = 0;
int newnode(int k){++ptr;s[ptr].maxl = k;return ptr;}
int siz[MAXL << 1];
void extend(int c)
{
int p = last;
int np = newnode(s[p].maxl + 1);siz[np] = 1;
for(;p && s[p].tr[c] == 0;p = s[p].par)s[p].tr[c] = np;
if(p == 0)s[np].par = root;
else
{
int q = s[p].tr[c];
if(s[p].maxl + 1 == s[q].maxl)s[np].par = q;
else
{
int nq = newnode(s[p].maxl + 1);
memcpy(s[nq].tr,s[q].tr,sizeof(s[q].tr));
s[nq].par = s[q].par;
s[q].par = s[np].par = nq;
for(;p && s[p].tr[c] == q;p = s[p].par)s[p].tr[c] = nq;
}
}
last = np;
return;
}
struct edge
{
int to,nxt;
}e[MAXL << 1];
int lin[MAXL << 1] = {0};
int edgenum = 0;
void add(int a,int b)
{
++edgenum;e[edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
return;
}
typedef long long ll;
ll res = 0;
void dfs(int k)
{
for(int i = lin[k];i != 0;i = e[i].nxt)
{
dfs(e[i].to);
siz[k] += siz[e[i].to];
}
if(siz[k] != 1)res = max(res,(ll)siz[k] * s[k].maxl);
return;
}
int main()
{
scanf("%s",c);
int l = strlen(c);
last = root = ptr = 1;
for(int i = 0;i < l;++i)extend(c[i] - 'a');
for(int i = 2;i <= ptr;++i)add(s[i].par,i);
dfs(1);
cout << res << endl;
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡