题目传送门
解题思路:
题意是求一个串各个长度的子串的可重叠最大重复出现次数。
①首先我们肯定能想到构造后缀自动机的时候每加入一个新的字符新产生的子串长度一定是跟当前节点有关。用cnt[]记录每个节点的串出现的次数,在一个个添加字符的时候,每次在这个新产生的后缀节点+1,我们知道如果这个节点没有对应符合要求的父节点的话还会再产生一个新的节点,那个节点不用记,因为那个节点也属于当前节点的后缀,我们等会儿从最底部叶子节点更新,这个点的值会被更新对,如果现在+1等会儿会重复。
②于是我们把整个串扔进后缀自动机之后,就把每次添加字符对应的节点都+1,那么怎么正确求得cnt[]呢?
由于sam(后缀自动机)中的节点不像pam(回文自动机)那样所有的儿子下标都比自己大,pam可以直接从下标大的更新,sam不可以。
但是本质还是按照先更新儿子,再更新父亲的顺序。
我们把所有节点按照节点字符串长度从大到小更新,儿子一定比父亲长,就保证了之前所说。很明显不能排序,排序直接超时,可以开个|S|个vector暴力存。
然后求得cnt[]之后,用类似差分的方法,sam上每个节点都有一个len记录长度,我们先用len更新答案ans[],最后倒着更新一遍即可。
意思就是:
_ 4 _ 3 _ _ 2 _ 1(len更新的答案)
倒着再更新一遍后:443322211
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define pt(x) printf("%s = %d\n",#x,x)
#define INF 0x3f3f3f3f
#define pb push_back
const int N = 250000+5;
char s[N];
const int maxn = 250000+5;
const int ALP = 26;
struct SAM
{
int trie[maxn<<1][ALP];
int fa[maxn<<1];
int len[maxn<<1];
int sz,last;
int cnt[maxn<<1];
int cf[maxn];//记录答案,差分取首字母...
vector<int>v[maxn];
void init(){
sz = last = 0;
newnode();
fa[0] = -1,len[0] = 0;
memset(cnt,0,sizeof cnt);
}
int newnode(){
memset(trie[sz],0,sizeof trie[sz]);
if (sz<=250000) v[sz].clear();//节点的下标<=长度,因此随用随初始化即可
return sz++;
}
int idx(char ch){
return ch-'a';
}
void add(char ch){
int c = idx(ch);
int p = last,np = newnode();
last = np;
len[np] = len[p]+1;
cnt[np]++;//只加np,而nq不加。因为nq是自己创建的辅助节点
v[len[np]].pb(np);
for (;~p && !trie[p][c];p = fa[p]) trie[p][c] = np;
if (p==-1) fa[np] = 0;
else {
int q = trie[p][c];
if (len[q]==len[p]+1) fa[np] = q;
else {
int nq = newnode();
fa[nq] = fa[q];
len[nq] = len[p]+1;
for (int i=0;i<ALP;i++) trie[nq][i] = trie[q][i];
fa[np] = fa[q] = nq;
for (;~p && trie[p][c]==q;p = fa[p]) trie[p][c] = nq;
v[len[nq]].pb(nq);
}
}
}
void count(int Len){
for (int i=1;i<=Len;i++) cf[i] = 1;
for (int i=Len;i>=1;i--)
for (int j=0;j<v[i].size();j++) cnt[fa[v[i][j]]] += cnt[v[i][j]];
for (int i=1;i<sz;i++) cf[len[i]] = max(cf[len[i]],cnt[i]);
for (int i=Len-1;i>=1;i--) cf[i] = max(cf[i],cf[i+1]);
for (int i=1;i<=Len;i++) printf("%d\n",cf[i]);
}
}sam;
int main()
{
while (~scanf("%s",s)){
sam.init();
int len = strlen(s);
for (int i=0;i<len;i++) sam.add(s[i]);
sam.count(len);
}
return 0;
}