Codeforces 917F Substrings in a String - 后缀自动机 - 分块 - bitset - KMP
Solution 1 Bitset
每次匹配一段,可以看成,依次考虑每个位置,匹配的位置对应的起点取交集。例如:
大概就这个意思。
bitset的count似乎很慢,可以用__builtin_popcount来数中间的位数,然后暴力数两端的位数会快很多。感觉手写倍增法数位数最快。但有人说前面那个内联函数比手写的$O(\log \log n)$的速度要快。
Code
1 /** 2 * Codeforces 3 * Problem#914F 4 * Accepted 5 * Time: 2760ms 6 * Memory: 4300k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int N = 1e5 + 5, alpha = 26; 13 14 int n, m; 15 char str[N], buf[N]; 16 bitset<N> ch[alpha], ans; 17 18 inline void init() { 19 scanf("%s", str + 1); 20 n = strlen(str + 1); 21 for (int i = 1; i <= n; i++) 22 ch[str[i] - 'a'][i] = 1; 23 scanf("%d", &m); 24 } 25 26 inline void solve() { 27 int opt, x, y, len; 28 while (m--) { 29 scanf("%d%d", &opt, &x); 30 if (opt == 1) { 31 scanf("%s", buf); 32 ch[str[x] - 'a'][x] = 0, ch[buf[0] - 'a'][x] = 1; 33 str[x] = buf[0]; 34 } else { 35 scanf("%d%s", &y, buf + 1); 36 len = strlen(buf + 1); 37 if (y - x + 1 < len) { 38 puts("0"); 39 continue; 40 } 41 ans.set(); 42 for (int i = 1; i <= len; i++) 43 ans &= (ch[buf[i] - 'a'] >> (i - 1)); 44 // for (int i = 1; i <= n; i++) 45 // cerr << ans[i] << " "; 46 // cerr << endl; 47 // for (int i = 1; i <= n; i++) 48 // cerr << (ans >> (x - 1))[i]; 49 // cerr << endl; 50 int res = (ans >> x).count() - (ans >> (y - len + 2)).count(); 51 printf("%d\n", res); 52 } 53 } 54 } 55 56 int main() { 57 init(); 58 solve(); 59 return 0; 60 }
Solution 2 Suffix Automaton , Block Division & KMP
这个是出题人的本意。估计出题人没有想到这道题竟然可以直接被bitset水掉。
对于在线数一个串的出现次数,排除所有非后缀数据结构。
由于后缀数据结构都不支持中间带修。因此考虑分块。每一块维护一个SAM。
要求修改的时候暴力重构一个块的SAM。
暂且钦定块大小为$C = \sqrt{n}$。
- 如果询问的串长大于$C$,由于询问总串长和$n$同阶,所以这一部分的询问数不会超过$\sqrt{n}$个,所以直接暴力KMP,时间复杂度$O(n^{1.5})$
- 如果询问的串长小于等于$C$,两端涉及到的位置暴力KMP,块间暴力KMP,块内在SAM中查询。这一部分的时间复杂度也是$O(n^{1.5})$
所以总时间复杂度为$O(n^{1.5})$、
由于SAM自带常数$26$(字符集大小),所以跑着很慢,sad..另外暴力的过程最好老老实实写KMP,千万不要像我一样直接用SAM来代替,然后无限TLE。。
Code
1 /** 2 * Codeforces 3 * Problem#917F 4 * Accepted 5 * Time: 2995ms 6 * Memory: 28428k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 typedef class TrieNode { 13 public: 14 int len, cnt; 15 TrieNode* ch[26]; 16 // map<char, TrieNode*> ch; 17 TrieNode* fail; 18 }TrieNode; 19 20 const int cs = 350, N = 1e5 + 5; 21 22 typedef class SuffixAutomaton { 23 public: 24 int maxlen; 25 TrieNode* pool; 26 int *cnt; 27 TrieNode** sp; 28 TrieNode *top; 29 TrieNode *rt, *last; 30 31 SuffixAutomaton(int maxlen = cs + 1):maxlen(maxlen) { 32 pool = new TrieNode[(maxlen * 2 + 5)]; 33 sp = new TrieNode*[(maxlen * 2 + 5)]; 34 cnt = new int[(maxlen + 1)]; 35 } 36 37 TrieNode* newnode(int len) { 38 // top->ch.clear(); 39 // cerr << top - pool << " " << maxlen << endl; 40 memset(top->ch, 0, sizeof(top->ch)); 41 top->len = len, top->cnt = 0; 42 top->fail = NULL; 43 return top++; 44 } 45 46 void reset() { 47 top = pool; 48 rt = newnode(0); 49 last = rt; 50 } 51 52 void extend(char c) { 53 int x = c - 'a'; 54 TrieNode* p = newnode(last->len + 1); 55 while (last && !last->ch[x]) 56 last->ch[x] = p, last = last->fail; 57 if (!last) 58 p->fail = rt; 59 else { 60 TrieNode *q = last->ch[x]; 61 if (q->len == last->len + 1) 62 p->fail = q; 63 else { 64 TrieNode* nq = newnode(last->len + 1); 65 nq->fail = q->fail, p->fail = nq, q->fail = nq; 66 // nq->ch = map<char, TrieNode*>(q->ch); 67 memcpy(nq->ch, q->ch, sizeof(nq->ch)); 68 while (last && last->ch[x] == q) 69 last->ch[x] = nq, last = last->fail; 70 } 71 } 72 p->cnt++, last = p; 73 } 74 75 void rebuild(char* str, int l, int r) { 76 reset(); 77 for (int i = l; i < r; i++) 78 extend(str[i]); 79 memset(cnt, 0, sizeof(int) * (r - l + 2)); 80 for (int i = 0; pool + i < top; i++) cnt[pool[i].len]++; 81 for (int i = 1; i <= r - l + 1; i++) cnt[i] += cnt[i - 1]; 82 for (int i = 0; pool + i < top; i++) 83 sp[(cnt[pool[i].len]--) - 1] = pool + i; 84 for (int i = top - pool - 1; i > 0; i--) sp[i]->fail->cnt += sp[i]->cnt; 85 } 86 87 int query(char *str) { 88 TrieNode* p = rt; 89 for (int i = 0; str[i] && p; i++) 90 p = p->ch[str[i] - 'a']; 91 return (p) ? (p->cnt) : (0); 92 } 93 }SuffixAutomaton; 94 95 int n, m, cc = 0; 96 int f[N]; 97 char str[N], buf[N]; 98 SuffixAutomaton sam[N / cs + 1]; 99 100 inline void init() { 101 scanf("%s", str); 102 n = strlen(str); 103 for (int i = cs; i < n; i += cs, cc++) 104 sam[cc].reset(), sam[cc].rebuild(str, i - cs, i); 105 scanf("%d", &m); 106 } 107 108 #define pick(p) ((l <= p && r >= p) ? (S[p]) : (0)) 109 110 int brute(char* S, char* T, int l, int r, int lenT) { 111 r += 1; 112 if (r - l < lenT) return 0; 113 f[0] = f[1] = 0; 114 for (int i = 1, j; i < lenT; i++) { 115 j = f[i]; 116 while (j && T[i] != T[j]) j = f[j]; 117 f[i + 1] = ((T[i] == T[j]) ? (j + 1) : (0)); 118 } 119 // for (int i = 0; i <= lenT; i++) 120 // cerr << f[i] << " "; 121 // cerr << endl; 122 int rt = 0; 123 for (int i = l, j = 0; i < r; i++) { 124 while (j && T[j] != S[i]) j = f[j]; 125 if (T[j] == S[i]) j++; 126 if (j == lenT) rt++, j = f[j]; 127 } 128 return rt; 129 } 130 131 inline void solve() { 132 int opt, x, y, len, xi, yi; 133 while (m--) { 134 scanf("%d%d", &opt, &x); 135 x--; 136 if (opt == 1) { 137 scanf("%s", buf); 138 xi = x / cs; 139 str[x] = buf[0]; 140 if (xi < cc) 141 sam[xi].rebuild(str, xi * cs, (xi + 1) * cs); 142 } else { 143 scanf("%d%s", &y, buf); 144 y -= 1, len = strlen(buf); 145 if (y - x + 1 < len) { 146 puts("0"); 147 continue; 148 } 149 xi = x / cs, yi = y / cs; 150 int res = 0; 151 if (len >= cs || xi == yi || xi == yi - 1) 152 res = brute(str, buf, x, y, len); 153 else { 154 res = brute(str, buf, x, xi * cs + cs + len - 2, len); 155 res += brute(str, buf, yi * cs - len + 1, y, len); 156 for (int i = xi + 1; i < yi; i++) 157 res += sam[i].query(buf); 158 for (int i = xi + 2; i < yi; i++) 159 res += brute(str, buf, i * cs - len + 1, i * cs + len - 2, len); 160 } 161 printf("%d\n", res); 162 } 163 } 164 } 165 166 int main() { 167 init(); 168 solve(); 169 return 0; 170 }