SPOJ NSUBSTR Substrings
后缀自动机SAM
题意
给一个字符串长度n,求他所有长度为k的子串中出现最多的串的出现次数。要求输出所有k的答案。
思路
SAM,拓扑排序统计每个状态的endposamu(即每个状态的字符串出现的次数)后:
求出每一个状态的|endpos(st)|后,我们还需要求出每个长度的子串最多出现了多少次。假设ans[l]表示长度为l的子串最多出现的次数。不需要对于每个状态st都循环一遍,利用|endpos(st)|更新ans[minlen(st)] … ans[maxlen(st)]的值。
值得注意的是ans[1], ans[2], … ans[length(S)]一定是一个单调递减序列。所以我们对于每个状态st,只需要更新ans[maxlen(st)]。之后令i = length(S)-1 .. 1,从后向前扫描一遍,令ans[i] = max(ans[i], ans[i+1]),即可。
代码
#include<bits/stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
typedef long long LL;
using namespace std;
const int MAXL=250005;
const int MAXS=26;
struct SAM
{
int n=0, st;
int maxlen[2*MAXL+10], minlen[2*MAXL+10], trans[2*MAXL+10][MAXS], slink[2*MAXL+10],
col[2*MAXL+10], indeg[2*MAXL+10], endposamu[2*MAXL+10], ans[MAXL];
int new_state(int _maxlen, int _minlen, int* _trans, int _slink)
{
n++;
maxlen[n]=_maxlen;
minlen[n]=_minlen;
for(int i=0; i<MAXS; i++)
{
if(_trans==NULL)
trans[n][i]=0;
else
trans[n][i]=_trans[i];
}
slink[n]=_slink;
return n;
}
int add_char(char ch, int u)
{
int c=ch-'a';
int z=new_state(maxlen[u]+1, -1, NULL, 0);
col[z]=1;
int v=u;
while(v!=0&&trans[v][c]==0)
{
trans[v][c]=z;
v=slink[v];
}
if(v==0)
{
minlen[z]=1;
slink[z]=1;
indeg[1]++;
return z;
}
int x=trans[v][c];
if(maxlen[v]+1==maxlen[x])
{
minlen[z]=maxlen[x]+1;
slink[z]=x;
indeg[x]++;
return z;
}
int y=new_state(maxlen[v]+1, -1, trans[x], slink[x]);
col[y]=0;
minlen[x]=maxlen[y]+1;
slink[x]=y;
minlen[z]=maxlen[y]+1;
slink[z]=y;
indeg[y]+=2;
int w=v;
while(w!=0&&trans[w][c]==x)
{
trans[w][c]=y;
w=slink[w];
}
minlen[y]=maxlen[slink[y]]+1;
return z;
}
void init()
{
memset(col, 0, sizeof(col));
memset(indeg, 0, sizeof(indeg));
memset(maxlen, 0, sizeof(maxlen));
memset(minlen, 0, sizeof(maxlen));
memset(trans, 0, sizeof(maxlen));
memset(slink, 0, sizeof(maxlen));
memset(endposamu, 0, sizeof(endposamu));
n=0;
st=new_state(0, -1, NULL, 0);
}
void getendpos()
{
queue<int> que;
for(int i=st;i<=n;i++)
{
if(indeg[i]==0) que.push(i);
if(col[i]==1) endposamu[i]++;
}
while(!que.empty())
{
int pos=que.front();que.pop();
endposamu[slink[pos]]+=endposamu[pos];
indeg[slink[pos]]--;
if(indeg[slink[pos]]==0) que.push(slink[pos]);
}
}
void addstring(char* s, int n)
{
int la=st;
for(int i=0;i<n;i++)
{
la=add_char(s[i], la);
}
getendpos();
for(int i=st;i<=this->n;i++)
ans[maxlen[i]]=max(ans[maxlen[i]], endposamu[i]);
for(int i=n-1;i>=1;i--)
ans[i]=max(ans[i], ans[i+1]);
for(int i=1;i<=n;i++)
printf("%d\n", ans[i]);
}
}sam;
char ss[MAXL*2+7];
int main()
{
sam.init();
scanf("%s", ss);int n=strlen(ss);
sam.addstring(ss, n);
//system("pause");
return 0;
}