1. 程式人生 > >Codeforces 917F Substrings in a String - 後綴自動機 - 分塊 - bitset - KMP

Codeforces 917F Substrings in a String - 後綴自動機 - 分塊 - bitset - KMP

aps rgba adding codeforce time max vision puts efi

題目傳送門

  傳送點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 }

Codeforces 917F Substrings in a String - 後綴自動機 - 分塊 - bitset - KMP