SPOJ NSUBSTR Substrings - 后缀自动机

题目传送门

  传送站I

  传送站II

题目大意

  给定一个字符串$S$,问对于任意整数$1\leqslant x\leqslant |S|$,长度为$x$的串最多出现了多少次。

  众所周知,我们可以用后缀自动机来求子串出现的次数。

  在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 }
posted @ 2018-03-25 15:21  阿波罗2003  阅读(284)  评论(0编辑  收藏  举报