SPOJ NSUBSTR Substrings - 后缀自动机
众所周知,我们可以用后缀自动机来求子串出现的次数。
在parent树上,叶节点向根节点累加$cnt$。想想这样做的本质,就是求出了每个状态的$Right$集合的大小。
根据parent指针的定义,子节点的Right集合是它的parent的Right集合的真子集,并且最短长度大于它的parent的最长长度。
所以,每次修改区间$[l, r]$的最大值,可以看成直接修改$[1, r]$。因此,可以差分一下,每次单点修改,最后做一次后缀和。
时间复杂度$O(|S|)$
Code
1 /** 2 * SPOJ 3 * Problem#NSUBSTR 4 * Accepted 5 * Time: 270ms 6 * Memory: 89088k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 #define smax(_a, _b) ((_a < _b) ? (_a = _b) : (0)) 12 13 const int N = 5e5 + 5, alpha = 26; 14 15 typedef class TrieNode { 16 public: 17 int len, cnt; 18 // TrieNode* ch[26]; 19 map<int, TrieNode*> ch; 20 TrieNode* fail; 21 }TrieNode; 22 23 TrieNode pool[N]; 24 TrieNode* sp[N]; 25 TrieNode* top = pool; 26 27 TrieNode* newnode(int len) { 28 top->len = len; 29 return top++; 30 } 31 32 typedef class SuffixAutomaton { 33 public: 34 TrieNode* rt; 35 TrieNode* last; 36 37 SuffixAutomaton() { 38 last = rt = newnode(0); 39 } 40 41 void extend(char x) { 42 int c = x - 'a'; 43 TrieNode *f = last, *p = newnode(last->len + 1); 44 while (f && !f->ch[c]) 45 f->ch[c] = p, f = f->fail; 46 if (!f) 47 p->fail = rt; 48 else { 49 TrieNode* q = f->ch[c]; 50 if (q->len == f->len + 1) 51 p->fail = q; 52 else { 53 TrieNode* nq = newnode(f->len + 1); 54 nq->fail = q->fail, q->fail = nq, p->fail = nq; 55 nq->ch = map<int, TrieNode*>(q->ch); 56 while (f && f->ch[c] == q) 57 f->ch[c] = nq, f = f->fail; 58 } 59 } 60 last = p, p->cnt = 1; 61 } 62 }SuffixAutomaton; 63 64 int n; 65 char str[N]; 66 int cnt[N], f[N]; 67 SuffixAutomaton sam; 68 69 inline void init() { 70 scanf("%s", str + 1); 71 n = strlen(str + 1); 72 } 73 74 inline void solve() { 75 for (int i = 1; i <= n; i++) 76 sam.extend(str[i]); 77 // for (TrieNode* p = pool; p < top; p++) 78 // cerr << p - pool << " " << p->fail - pool << " " << p->len << " " << p->cnt << endl; 79 for (TrieNode* p = pool + 1; p < top; p++) cnt[p->len]++; 80 for (int i = 2; i <= n; i++) cnt[i] += cnt[i - 1]; 81 for (int i = 1; pool + i < top; i++) sp[cnt[pool[i].len]--] = pool + i; 82 for (int i = top - pool - 1; i; i--) sp[i]->fail->cnt += sp[i]->cnt; 83 for (int i = 1; pool + i < top; i++) smax(f[sp[i]->len], sp[i]->cnt); 84 for (int i = n; i > 1; i--) smax(f[i - 1], f[i]); 85 for (int i = 1; i <= n; i++) 86 printf("%d\n", f[i]); 87 } 88 89 int main() { 90 init(); 91 solve(); 92 return 0; 93 }