题目传送门

  传送点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 + , alpha = ;
  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 + );
  20. n = strlen(str + );
  21. for (int i = ; i <= n; i++)
  22. ch[str[i] - 'a'][i] = ;
  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 == ) {
  31. scanf("%s", buf);
  32. ch[str[x] - 'a'][x] = , ch[buf[] - 'a'][x] = ;
  33. str[x] = buf[];
  34. } else {
  35. scanf("%d%s", &y, buf + );
  36. len = strlen(buf + );
  37. if (y - x + < len) {
  38. puts("");
  39. continue;
  40. }
  41. ans.set();
  42. for (int i = ; i <= len; i++)
  43. ans &= (ch[buf[i] - 'a'] >> (i - ));
  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 + )).count();
  51. printf("%d\n", res);
  52. }
  53. }
  54. }
  55.  
  56. int main() {
  57. init();
  58. solve();
  59. return ;
  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[];
  16. // map<char, TrieNode*> ch;
  17. TrieNode* fail;
  18. }TrieNode;
  19.  
  20. const int cs = , N = 1e5 + ;
  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 + ):maxlen(maxlen) {
  32. pool = new TrieNode[(maxlen * + )];
  33. sp = new TrieNode*[(maxlen * + )];
  34. cnt = new int[(maxlen + )];
  35. }
  36.  
  37. TrieNode* newnode(int len) {
  38. // top->ch.clear();
  39. // cerr << top - pool << " " << maxlen << endl;
  40. memset(top->ch, , sizeof(top->ch));
  41. top->len = len, top->cnt = ;
  42. top->fail = NULL;
  43. return top++;
  44. }
  45.  
  46. void reset() {
  47. top = pool;
  48. rt = newnode();
  49. last = rt;
  50. }
  51.  
  52. void extend(char c) {
  53. int x = c - 'a';
  54. TrieNode* p = newnode(last->len + );
  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 + )
  62. p->fail = q;
  63. else {
  64. TrieNode* nq = newnode(last->len + );
  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, , sizeof(int) * (r - l + ));
  80. for (int i = ; pool + i < top; i++) cnt[pool[i].len]++;
  81. for (int i = ; i <= r - l + ; i++) cnt[i] += cnt[i - ];
  82. for (int i = ; pool + i < top; i++)
  83. sp[(cnt[pool[i].len]--) - ] = pool + i;
  84. for (int i = top - pool - ; i > ; i--) sp[i]->fail->cnt += sp[i]->cnt;
  85. }
  86.  
  87. int query(char *str) {
  88. TrieNode* p = rt;
  89. for (int i = ; str[i] && p; i++)
  90. p = p->ch[str[i] - 'a'];
  91. return (p) ? (p->cnt) : ();
  92. }
  93. }SuffixAutomaton;
  94.  
  95. int n, m, cc = ;
  96. int f[N];
  97. char str[N], buf[N];
  98. SuffixAutomaton sam[N / cs + ];
  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 += ;
  112. if (r - l < lenT) return ;
  113. f[] = f[] = ;
  114. for (int i = , j; i < lenT; i++) {
  115. j = f[i];
  116. while (j && T[i] != T[j]) j = f[j];
  117. f[i + ] = ((T[i] == T[j]) ? (j + ) : ());
  118. }
  119. // for (int i = 0; i <= lenT; i++)
  120. // cerr << f[i] << " ";
  121. // cerr << endl;
  122. int rt = ;
  123. for (int i = l, j = ; 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 == ) {
  137. scanf("%s", buf);
  138. xi = x / cs;
  139. str[x] = buf[];
  140. if (xi < cc)
  141. sam[xi].rebuild(str, xi * cs, (xi + ) * cs);
  142. } else {
  143. scanf("%d%s", &y, buf);
  144. y -= , len = strlen(buf);
  145. if (y - x + < len) {
  146. puts("");
  147. continue;
  148. }
  149. xi = x / cs, yi = y / cs;
  150. int res = ;
  151. if (len >= cs || xi == yi || xi == yi - )
  152. res = brute(str, buf, x, y, len);
  153. else {
  154. res = brute(str, buf, x, xi * cs + cs + len - , len);
  155. res += brute(str, buf, yi * cs - len + , y, len);
  156. for (int i = xi + ; i < yi; i++)
  157. res += sam[i].query(buf);
  158. for (int i = xi + ; i < yi; i++)
  159. res += brute(str, buf, i * cs - len + , i * cs + len - , len);
  160. }
  161. printf("%d\n", res);
  162. }
  163. }
  164. }
  165.  
  166. int main() {
  167. init();
  168. solve();
  169. return ;
  170. }

Codeforces 917F Substrings in a String - 后缀自动机 - 分块 - bitset - KMP的更多相关文章

  1. Common Substrings POJ - 3415 (后缀自动机)

    Common Substrings \[ Time Limit: 5000 ms\quad Memory Limit: 65536 kB \] 题意 给出两个字符串,要求两个字符串公共子串长度不小于 ...

  2. Substrings SPOJ - NSUBSTR (后缀自动机)

    Substrings \[ Time Limit: 100ms\quad Memory Limit: 1572864 kB \] 题意 给出一个长度为 \(250000\) 的字符串,求出所有 \(x ...

  3. CodeForces - 616F:Expensive Strings (后缀自动机)

    You are given n strings ti. Each string has cost ci. Let's define the function of string , where ps, ...

  4. 【hihocoder#1413】Rikka with String 后缀自动机 + 差分

    搞了一上午+接近一下午这个题,然后被屠了个稀烂,默默仰慕一晚上学会SAM的以及半天4道SAM的hxy大爷. 题目链接:http://hihocoder.com/problemset/problem/1 ...

  5. HackerRank Special Substrings 回文树+后缀自动机+set

    传送门 既然要求对每个前缀都求出答案,不难想到应该用回文树求出所有本质不同的回文子串. 然后考虑如何对这些回文子串的前缀进行去重. 结论:答案等于所有本质不同的回文子串长之和减去字典序相邻的回文子串的 ...

  6. 【CodeForces - 235C】Cyclical Quest 【后缀自动机】

    题意 给出一个字符串s1和q个询问,每个询问给出一个字符串s2,问这个询问的字符串的所有不同的周期串在s1中出现的次数的和. 分析 对于s1建后缀自动机.对于询问的每个字符串s2,我们按照处理循环串的 ...

  7. 牛客多校第四场 I string 后缀自动机/回文自动机

    这个回文自动机的板有问题,它虽然能过这道题,但是在计算size的时候会出锅! 题意: 求一个字符串中本质不同的连续子串有几个,但是某串和它反转后的字符串算一个. 题解: 要注意的是,一般字符串题中的“ ...

  8. Codeforces 427D Match &amp; Catch(后缀自动机)

    [题目链接] http://codeforces.com/problemset/problem/427/D [题目大意] 给出一个两个字符串,求出最短且在两个字符串中唯一的公共子串. [题解] 以原字 ...

  9. 识别子串 (string)——后缀自动机+线段树

    题目 [题目描述] 一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当: 1.$ i \leq x \leq j ...

随机推荐

  1. cocos2d-x android工程接入第三方支付宝SDK

    1. 首先去支付宝官网下载开发者文档 2. 然后按着开发者文档将支付宝的sdk导入到你的工程中,并关联到工程中,步骤入下图: (1)将从支付宝官方网站获得的支付宝的sdk的jar包拷贝到工程中的lib ...

  2. yield和send函数

    yield作用类似于return,其本质是一个迭代器. 当程序执行到yield时,会结束本次循环,返回一个值,然后内置含有next()函数, 下次在执行时,会从yield结束的地方继续执行. 带yie ...

  3. LeetCode14.最长公共前缀

    编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow" ...

  4. 用C#创建一个窗体,在构造函数里面写代码和在from_load事件里面写代码有什么不同?

    没太大区别.一区别就是代码加载时间先后的问题.构造函数先加载,load事件中后加载.

  5. jQuery-手风琴效果-2

    动画 高级函数:基于底层函数又进行了封装 两大块:简化版的动画函数和万能动画函数 简化版动画函数 显示/隐藏$().show; $(...).hide(); 强调:无参数的show()/hide()使 ...

  6. maven下载和安装

    注意:安装Maven3之前需要安装jdk1.7以上版本,下面介绍的是最新版Maven官网下载并安装, 每个人使用的编辑器不同,在这里我就不介绍了,可以去网上查对应编辑器Maven配置方法. 第一步,官 ...

  7. MySQL练习题之参考答案

    1.创建表结构和数据 /* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Sou ...

  8. ==与Equals的作用

    string str1 = "Blackteeth"; string str2 = str1; string str3 = "Blackteeth"; Cons ...

  9. PGPDesktop在win7环境下的安装和使用

    PGPDesktop在win7环境下的安装和使用 PGP的简介 PGP(Pretty Good Privacy),是一个基于RSA公钥加密体系的邮件加密软件,它提供了非对称加密和数字签名,是目前非常流 ...

  10. MySql获取两个日期间的时间差

    [1]MySql 语言获取两个日期间的时间差 DATEDIFF 函数可以获得两个日期之间的时间差.但是,这个函数得出的结果是天数. 需要直接获取秒数可使用TIMESTAMPDIFF函数.应用示例如下: ...