题目大意:给你一个长度为n的字符串,求出所有不同长度的字符串出现的最大次数。
n<=250000
如:abaaa
输出:
4
2
1
1
1
spoj上的时限卡的太严,必须使用O(N)的算法那才能过掉,所以采用后缀自动机解决。
一个字符串出现的次数,即为后缀自动机中该字符串对应的节点的right集合的大小,right集合的大小等于子树中叶子节点的数目。
令dp[i]表示长度为i的字符串出现的最大次数。dp[i]可以通过在后缀链接树中从叶子节点到根节点依次求出。
最后,按长度从大到小,用dp[i]去更新dp[i-1]。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; #define MAXN 250005 #define MAXC 26 struct node { int go[MAXC],sufflink,step,right; void clear() {memset(go,0,sizeof go); sufflink=step=right=0; } }tree[MAXN<<1]; int dp[MAXN],cnt[MAXN],rk[MAXN<<1],root,tot,cur,last,len,ans; char s[MAXN]; void insert(int val) { int p,q; tree[last].go[val]=++tot; cur=tot; tree[cur].clear(); tree[cur].step=tree[last].step+1; tree[cur].right++; for(p=tree[last].sufflink;p&&tree[p].go[val]==0;p=tree[p].sufflink) tree[p].go[val]=cur; if(p==0) tree[cur].sufflink=root; else { q=tree[p].go[val]; if(tree[p].step+1==tree[q].step) tree[cur].sufflink=q; else { tree[++tot].clear(); tree[tot]=tree[q]; tree[tot].right=0; tree[tot].step=tree[p].step+1; tree[cur].sufflink=tree[q].sufflink=tot; for(;tree[p].go[val]==q;p=tree[p].sufflink) tree[p].go[val]=tot; } } last=cur; } void calc() { int temp; for(int i=1;i<=tot;i++)cnt[tree[i].step]++; for(int i=1;i<=len;i++)cnt[i]+=cnt[i-1]; for(int i=1;i<=tot;i++)rk[cnt[tree[i].step]--]=i; for(int i=tot;i>1;i--) { temp=tree[rk[i]].sufflink; if(tree[rk[i]].right>dp[tree[rk[i]].step])dp[tree[rk[i]].step]=tree[rk[i]].right; if(temp)tree[temp].right+=tree[rk[i]].right; } for(int i=len-1;i>=1;i--) if(dp[i+1]>dp[i])dp[i]=dp[i+1]; for(int i=1;i<=len;i++) printf("%d ",dp[i]); } int main() { while(~scanf("%s",s)) { root=last=cur=tot=1; tree[1].clear(); memset(dp,0,sizeof dp); memset(cnt,0,sizeof cnt); len=strlen(s); for(int i=0;i<len;i++) insert(s[i]-'a'); calc(); } return 0;