http://acm.hust.edu.cn/vjudge/problem/28005
题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。求F(1)..F(Length(S)) 。
题解:
关键问题在于统计某个串出现了多少次。
在后缀自动机中,答案即为包含了这个串的状态的right集合的大小。
后缀自动机有两张DAG,一张是trans图,一张是parent树
从trans图的角度出发,right集合的大小为该状态走到结束状态的方案数
从parent树的角度出发,parent树是反串的后缀树,right集合的大小为该状态的子树中有多少个结点代表了反串的一个后缀(也就是原串的前缀)
我们采取第二种计数方法,先将包含原串前缀的状态的right集合设为1(相当于在反串的后缀树中将后缀结点标记为1)
因为parent树中我们并没有把儿子记下来,所以没办法直接对parent树bfs,但我们知道儿子的len严格大于父亲的len,所以我们按len的长度进行排序,然后按len从大到小,用当前点更新parent的答案
我们发现一个结点代表的长度是一个区间,我们先只考虑该结点代表的最长长度,然后我们再用长串去更新短串(因为一个长串出现k次,它的所有后缀都至少出现k次)即可————引用自http://blog.csdn.net/hbhcy98/article/details/51055733
首先要求节点x表示的最长的子串,也就是长度为step[x]的这个串出现了多少次——该节点的right集合。
right集合到底怎么求?
从parent树的角度出发,parent树是反串的后缀树,right集合的大小为该状态的子树中有多少个结点代表了反串的一个后缀(也就是原串的前缀)
在建好的自动机上跑一遍原串,经过的节点r[x]=1;
然后找出自动机的拓扑序,按着拓扑序的逆序for一遍,更新pre[x]。
一个节点贡献的子串长度区间是[min[x],max[x]],我们开始只考虑了该节点代表的最大长度max[x],也就是长度为step[x]这个子串。
然后我们用长串去更新短串。
因为sam是在线的,从root开始跳到x节点的路径必定是主链上root到x这条最长串(原串的前缀)的后缀。
一个长串出现k次,它的所有后缀都至少出现k次。
f[i-1]=maxx(f[i-1],f[i]);
机智啊。。。。。。我看了好几个题解才看懂。。。。TAT。。。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<iostream>
5 #include<queue>
6 #include<ctime>
7 using namespace std;
8
9 const int N=2*250010;
10 char s[N];
11 int tot,last,sl,cl;
12 int son[N][30],pre[N],step[N],in[N],c[N],r[N],f[N];
13 bool vis[N];
14 queue<int> Q;
15
16 int maxx(int x,int y){return x>y ? x:y;}
17
18 int add_node(int x){step[++tot]=x;/*r[tot]=1;*/return tot;}
19
20 void clear()
21 {
22 memset(r,0,sizeof(r));
23 memset(son,0,sizeof(son));
24 memset(pre,0,sizeof(pre));
25 memset(step,0,sizeof(step));
26 tot=0;add_node(0);last=1;
27 }
28
29 void extend(int ch)
30 {
31 int p=last,np=add_node(step[p]+1);
32 while(p && !son[p][ch]) son[p][ch]=np,in[np]++,p=pre[p];
33 if(!p) pre[np]=1;
34 else
35 {
36 int q=son[p][ch];
37 if(step[q]==step[p]+1) pre[np]=q;
38 else
39 {
40 int nq=add_node(step[p]+1);
41 memcpy(son[nq],son[q],sizeof(son[q]));
42 for(int i=1;i<=26;i++)
43 if(son[q][i]) in[son[q][i]]++;
44 pre[nq]=pre[q];
45 pre[np]=pre[q]=nq;
46 while(son[p][ch]==q) son[p][ch]=nq,in[nq]++,in[q]--,p=pre[p];
47 }
48 }
49 last=np;
50 }
51
52 void find_tp()
53 {
54 while(!Q.empty()) Q.pop();
55 memset(vis,0,sizeof(vis));
56 Q.push(1);vis[1]=1;cl=0;
57 while(!Q.empty())
58 {
59 int x=Q.front();vis[x]=0;c[++cl]=x;Q.pop();
60 for(int i=1;i<=26;i++)
61 {
62 int y=son[x][i];
63 if(!y) continue;
64 in[y]--;
65 if(!in[y] && !vis[y]) vis[y]=1,Q.push(y);
66 }
67 }
68 }
69
70 int main()
71 {
72 freopen("a.in","r",stdin);
73 scanf("%s",s+1);
74 sl=strlen(s+1);
75 clear();
76 for(int i=1;i<=sl;i++) extend(s[i]-'a'+1);
77 int x=1,ch;
78 for(int i=1;i<=sl;i++)
79 {
80 ch=s[i]-'a'+1;
81 x=son[x][ch];
82 r[x]++;
83 }
84 find_tp();
85 for(int i=cl;i>=1;i--)
86 {
87 x=c[i];
88 r[pre[x]]+=r[x];
89 // printf("r %d = %d step = %d
",x,r[x],step[x]);
90 f[step[x]]=maxx(f[step[x]],r[x]);
91 }
92 // for(int i=1;i<=sl;i++) printf("%d ",f[i]);printf("
");
93 for(int i=sl;i>=2;i--) f[i-1]=maxx(f[i],f[i-1]);
94 for(int i=1;i<=sl;i++) printf("%d
",f[i]);
95 return 0;
96 }