Codeforces 917F Substrings in a String - 后缀自动机 - 分块 - bitset - KMP

题目传送门

  传送点I

  传送点II

  传送点III

题目大意

   给定一个字母串,要求支持以下操作:

  1. 修改一个位置的字母
  2. 查询一段区间中,字符串$s$作为子串出现的次数

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 }
bitset

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 }
posted @ 2018-03-30 23:03  阿波罗2003  阅读(272)  评论(0编辑  收藏  举报