题目传送门

  传送点I

  传送点II

  传送点III

题目大意

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

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

Solution 1 Bitset

  每次匹配一段,可以看成,依次考虑每个位置,匹配的位置对应的起点取交集。例如:

  大概就这个意思。

  bitset的count似乎很慢,可以用__builtin_popcount来数中间的位数,然后暴力数两端的位数会快很多。感觉手写倍增法数位数最快。但有人说前面那个内联函数比手写的$O(\log \log n)$的速度要快。

Code

 /**
* Codeforces
* Problem#914F
* Accepted
* Time: 2760ms
* Memory: 4300k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 1e5 + , alpha = ; int n, m;
char str[N], buf[N];
bitset<N> ch[alpha], ans; inline void init() {
scanf("%s", str + );
n = strlen(str + );
for (int i = ; i <= n; i++)
ch[str[i] - 'a'][i] = ;
scanf("%d", &m);
} inline void solve() {
int opt, x, y, len;
while (m--) {
scanf("%d%d", &opt, &x);
if (opt == ) {
scanf("%s", buf);
ch[str[x] - 'a'][x] = , ch[buf[] - 'a'][x] = ;
str[x] = buf[];
} else {
scanf("%d%s", &y, buf + );
len = strlen(buf + );
if (y - x + < len) {
puts("");
continue;
}
ans.set();
for (int i = ; i <= len; i++)
ans &= (ch[buf[i] - 'a'] >> (i - ));
// for (int i = 1; i <= n; i++)
// cerr << ans[i] << " ";
// cerr << endl;
// for (int i = 1; i <= n; i++)
// cerr << (ans >> (x - 1))[i];
// cerr << endl;
int res = (ans >> x).count() - (ans >> (y - len + )).count();
printf("%d\n", res);
}
}
} int main() {
init();
solve();
return ;
}

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

 /**
* Codeforces
* Problem#917F
* Accepted
* Time: 2995ms
* Memory: 28428k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; typedef class TrieNode {
public:
int len, cnt;
TrieNode* ch[];
// map<char, TrieNode*> ch;
TrieNode* fail;
}TrieNode; const int cs = , N = 1e5 + ; typedef class SuffixAutomaton {
public:
int maxlen;
TrieNode* pool;
int *cnt;
TrieNode** sp;
TrieNode *top;
TrieNode *rt, *last; SuffixAutomaton(int maxlen = cs + ):maxlen(maxlen) {
pool = new TrieNode[(maxlen * + )];
sp = new TrieNode*[(maxlen * + )];
cnt = new int[(maxlen + )];
} TrieNode* newnode(int len) {
// top->ch.clear();
// cerr << top - pool << " " << maxlen << endl;
memset(top->ch, , sizeof(top->ch));
top->len = len, top->cnt = ;
top->fail = NULL;
return top++;
} void reset() {
top = pool;
rt = newnode();
last = rt;
} void extend(char c) {
int x = c - 'a';
TrieNode* p = newnode(last->len + );
while (last && !last->ch[x])
last->ch[x] = p, last = last->fail;
if (!last)
p->fail = rt;
else {
TrieNode *q = last->ch[x];
if (q->len == last->len + )
p->fail = q;
else {
TrieNode* nq = newnode(last->len + );
nq->fail = q->fail, p->fail = nq, q->fail = nq;
// nq->ch = map<char, TrieNode*>(q->ch);
memcpy(nq->ch, q->ch, sizeof(nq->ch));
while (last && last->ch[x] == q)
last->ch[x] = nq, last = last->fail;
}
}
p->cnt++, last = p;
} void rebuild(char* str, int l, int r) {
reset();
for (int i = l; i < r; i++)
extend(str[i]);
memset(cnt, , sizeof(int) * (r - l + ));
for (int i = ; pool + i < top; i++) cnt[pool[i].len]++;
for (int i = ; i <= r - l + ; i++) cnt[i] += cnt[i - ];
for (int i = ; pool + i < top; i++)
sp[(cnt[pool[i].len]--) - ] = pool + i;
for (int i = top - pool - ; i > ; i--) sp[i]->fail->cnt += sp[i]->cnt;
} int query(char *str) {
TrieNode* p = rt;
for (int i = ; str[i] && p; i++)
p = p->ch[str[i] - 'a'];
return (p) ? (p->cnt) : ();
}
}SuffixAutomaton; int n, m, cc = ;
int f[N];
char str[N], buf[N];
SuffixAutomaton sam[N / cs + ]; inline void init() {
scanf("%s", str);
n = strlen(str);
for (int i = cs; i < n; i += cs, cc++)
sam[cc].reset(), sam[cc].rebuild(str, i - cs, i);
scanf("%d", &m);
} #define pick(p) ((l <= p && r >= p) ? (S[p]) : (0)) int brute(char* S, char* T, int l, int r, int lenT) {
r += ;
if (r - l < lenT) return ;
f[] = f[] = ;
for (int i = , j; i < lenT; i++) {
j = f[i];
while (j && T[i] != T[j]) j = f[j];
f[i + ] = ((T[i] == T[j]) ? (j + ) : ());
}
// for (int i = 0; i <= lenT; i++)
// cerr << f[i] << " ";
// cerr << endl;
int rt = ;
for (int i = l, j = ; i < r; i++) {
while (j && T[j] != S[i]) j = f[j];
if (T[j] == S[i]) j++;
if (j == lenT) rt++, j = f[j];
}
return rt;
} inline void solve() {
int opt, x, y, len, xi, yi;
while (m--) {
scanf("%d%d", &opt, &x);
x--;
if (opt == ) {
scanf("%s", buf);
xi = x / cs;
str[x] = buf[];
if (xi < cc)
sam[xi].rebuild(str, xi * cs, (xi + ) * cs);
} else {
scanf("%d%s", &y, buf);
y -= , len = strlen(buf);
if (y - x + < len) {
puts("");
continue;
}
xi = x / cs, yi = y / cs;
int res = ;
if (len >= cs || xi == yi || xi == yi - )
res = brute(str, buf, x, y, len);
else {
res = brute(str, buf, x, xi * cs + cs + len - , len);
res += brute(str, buf, yi * cs - len + , y, len);
for (int i = xi + ; i < yi; i++)
res += sam[i].query(buf);
for (int i = xi + ; i < yi; i++)
res += brute(str, buf, i * cs - len + , i * cs + len - , len);
}
printf("%d\n", res);
}
}
} int main() {
init();
solve();
return ;
}

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. cocos2dx (关于斗地主人物偏移位置)

    就是说不管是谁登陆游戏,你的人物信息资料始终在平板电脑的屏幕正下方(位置坐标需要自己设定,我设置定的是0号位() char LandLordsScene::getUIPosition(char pos ...

  2. 关于js语句的分号

    我在使用js的时候可能发现一个现象:js语句结尾有时候有分号,有时候没有,没有的时候js代码也是能正确执行的. 到底要不要写分号?QAQ 转自博客园@winter-cn JavaScript自动加分号 ...

  3. JS实例4

    根据当前年的前五年后五年的年月日 <select id="nian" onclick="Bian()"></select>年 <s ...

  4. cache基础

    cache是系统中的一块快速SRAM,价格高,但是访问速度快,可以减少CPU到main memory的latency. cache中的术语有: 1) Cache hits,表示可以在cache中,查找 ...

  5. ling join 报错The specified LINQ expression contains references to queries that are associated with different cont

    The specified LINQ expression contains references to queries that are associated with different cont ...

  6. Vue系列之 => 自定义键盘修饰符

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Python2.6 升级2.7

    一. Centos6 默认为python2.6且不可卸载(因为Centos6深度依赖Python),要想升级为2.7 只能通过全新升级 操作如下: 1.下载 Python2.7 网址 https:// ...

  8. Windows10上安装Keras 和 TensorFlow-GPU

    安装环境: Windows 10 64bit GPU: GeForce gt 720 Python: 3.5.3 CUDA: 8 首先下载Anaconda3的Win10 64bit版,安装Python ...

  9. JavaScript--Document对象方法createElement()和createTextNode()

    createElement() 方法通过指定名称创建一个元素 createTextNode() 可创建文本节点 <!DOCTYPE html> <html> <head& ...

  10. .net 缓存

    缓存有很多实现方法,所有这些可以被分为两类,基于内存的缓存和基于磁盘的缓存: 1.  内存驻留缓存——包含在内存中临时存储数据的所有实现方法,通常在以下情况下使用: a)       应用程序频繁使用 ...