模板
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;