后缀自动机

模板

 1 struct SAM{
 2     static const int MAXN = 100001<<1;//大小为字符串长度两倍
 3     static const int LetterSize = 26;
 4 
 5     int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN];
 6     int sum[MAXN], tp[MAXN], cnt[MAXN]; //sum,tp用于拓扑排序,tp为排序后的数组
 7     //tp 长度拓扑序后的位置
 8     //cnt
 9     /*1.对于每个节点,父节点的接受串是该节点的接受串的后缀,而且是最长的那个。
10     所以呢,用该节点的len减去父节点的fa.len,就是该节点表示的串中父节点不能表示的串个数。
11     当某节点的计数到达k次时,就说明有len-fa.len个不同子串计数到达k次了。
12     (同理,当插入这个节点,出现的新不同子串个数就是len-fa.len。这也是很有用的性质。)
13 
14     2.每个节点表示串出现的次数,是这个节点子树上有效节点的总数。
15     (有效节点指插入该字符产生的那个节点,也就是代码中extend中的np节点)。
16     这样我们初始化有效节点为1,然后拓扑排序dp,就可以O(n)得到每个节点出现次数。*/
17     void init( void){
18         last = tot = 1;
19         len[1] = 0;
20         memset(ch,0,sizeof ch);
21         memset(fa,0,sizeof fa);
22         memset(cnt,0,sizeof cnt);
23     }
24 
25     void add( int x){
26         int p = last, np = last = ++tot;
27         len[np] = len[p] + 1, cnt[last] = 1;
28         memset( ch[np], 0, sizeof ch[np]);
29         while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
30         if( p == 0)
31             fa[np] = 1;
32         else{
33             int q = ch[p][x];
34             if( len[q] == len[p] + 1)
35                 fa[np] = q;
36             else{
37                 int nq = ++tot;
38                 memcpy( ch[nq], ch[q], sizeof ch[q]);
39                 len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
40                 while( p && ch[p][x] == q)  ch[p][x] = nq, p = fa[p];
41             }
42         }
43     }
44 
45     void toposort(){
46         for(int i = 1; i <= len[last]; i++)   sum[i] = 0;
47         for(int i = 1; i <= tot; i++)   sum[len[i]]++;
48         for(int i = 1; i <= len[last]; i++)   sum[i] += sum[i-1];
49         for(int i = 1; i <= tot; i++)   tp[sum[len[i]]--] = i;
50         for(int i = tot; i; i--)   cnt[fa[tp[i]]] += cnt[tp[i]];
51     }
52 
53 } sam;

 

posted @ 2017-10-16 17:34  yZi  阅读(123)  评论(0编辑  收藏  举报