【spoj8222-Substrings】sam求子串出现次数

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 }
原文地址:https://www.cnblogs.com/KonjakJuruo/p/5840219.html