题意
给出一个字符串,要你找出所有长度的子串分别的最多出现次数。
分析
我们建出后缀自动机,然后预处理出每个状态的cnt,cnt[u]指的是u这个状态的right集合大小。我们设f[len]为长度为len的子串的最多出现次数。我们对于自动机的每个状态都更新f,f[st[u].len]=max(f[st[u].len],cnt[u])。然后这样更新完以后,可以神奇的dp一下。f[len]=max(f[len],f[len+1]).想想为什么?
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=250000+100; 8 char s[maxn]; 9 int n; 10 struct state{ 11 int len,link; 12 int ch[26]; 13 }st[maxn*2]; 14 int sz,last,cur,cnt[2*maxn],c[2*maxn],f[maxn]; 15 void init(){ 16 cur=last=0; 17 sz=1; 18 st[0].link=-1; 19 st[0].len=0; 20 memset(st[0].ch,-1,sizeof(st[0].ch)); 21 } 22 void build_sam(int c){ 23 cur=sz++; 24 st[cur].len=st[last].len+1; 25 cnt[cur]=1; 26 memset(st[cur].ch,-1,sizeof(st[cur].ch)); 27 int p; 28 for(p=last;p!=-1&&st[p].ch[c]==-1;p=st[p].link) 29 st[p].ch[c]=cur; 30 if(p==-1) 31 st[cur].link=0; 32 else{ 33 int q=st[p].ch[c]; 34 if(st[q].len==st[p].len+1) 35 st[cur].link=q; 36 else{ 37 int clone=sz++; 38 st[clone].len=st[p].len+1; 39 st[clone].link=st[q].link; 40 for(int i=0;i<26;i++) 41 st[clone].ch[i]=st[q].ch[i]; 42 for(;p!=-1&&st[p].ch[c]==q;p=st[p].link) 43 st[p].ch[c]=clone; 44 st[q].link=st[cur].link=clone; 45 } 46 } 47 last=cur; 48 } 49 int cmp(int a,int b){ 50 return st[a].len>st[b].len; 51 } 52 int main(){ 53 scanf("%s",s); 54 n=strlen(s); 55 init(); 56 for(int i=0;i<n;i++) 57 build_sam(s[i]-'a'); 58 for(int i=0;i<sz;i++) 59 c[i]=i; 60 sort(c,c+sz,cmp); 61 // for(int i=0;i<sz;i++){ 62 // printf("%d ",st[i].link); 63 // } 64 // printf("!! "); 65 66 for(int i=0;i<sz;i++){ 67 int o=c[i]; 68 cnt[st[o].link]+=cnt[o]; 69 } 70 // for(int i=0;i<sz;i++) 71 // printf("%d ",cnt[i]); 72 // printf(" "); 73 74 for(int i=0;i<sz;i++) 75 f[st[i].len]=max(f[st[i].len],cnt[i]); 76 for(int i=n-1;i>=1;i--) 77 f[i]=max(f[i],f[i+1]); 78 for(int i=1;i<=n;i++){ 79 printf("%d ",f[i]); 80 } 81 return 0; 82 }