Codeforces 917F Substrings in a String - 后缀自动机 - 分块 - bitset - KMP
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的更多相关文章
- Common Substrings POJ - 3415 (后缀自动机)
Common Substrings \[ Time Limit: 5000 ms\quad Memory Limit: 65536 kB \] 题意 给出两个字符串,要求两个字符串公共子串长度不小于 ...
- Substrings SPOJ - NSUBSTR (后缀自动机)
Substrings \[ Time Limit: 100ms\quad Memory Limit: 1572864 kB \] 题意 给出一个长度为 \(250000\) 的字符串,求出所有 \(x ...
- CodeForces - 616F:Expensive Strings (后缀自动机)
You are given n strings ti. Each string has cost ci. Let's define the function of string , where ps, ...
- 【hihocoder#1413】Rikka with String 后缀自动机 + 差分
搞了一上午+接近一下午这个题,然后被屠了个稀烂,默默仰慕一晚上学会SAM的以及半天4道SAM的hxy大爷. 题目链接:http://hihocoder.com/problemset/problem/1 ...
- HackerRank Special Substrings 回文树+后缀自动机+set
传送门 既然要求对每个前缀都求出答案,不难想到应该用回文树求出所有本质不同的回文子串. 然后考虑如何对这些回文子串的前缀进行去重. 结论:答案等于所有本质不同的回文子串长之和减去字典序相邻的回文子串的 ...
- 【CodeForces - 235C】Cyclical Quest 【后缀自动机】
题意 给出一个字符串s1和q个询问,每个询问给出一个字符串s2,问这个询问的字符串的所有不同的周期串在s1中出现的次数的和. 分析 对于s1建后缀自动机.对于询问的每个字符串s2,我们按照处理循环串的 ...
- 牛客多校第四场 I string 后缀自动机/回文自动机
这个回文自动机的板有问题,它虽然能过这道题,但是在计算size的时候会出锅! 题意: 求一个字符串中本质不同的连续子串有几个,但是某串和它反转后的字符串算一个. 题解: 要注意的是,一般字符串题中的“ ...
- Codeforces 427D Match & Catch(后缀自动机)
[题目链接] http://codeforces.com/problemset/problem/427/D [题目大意] 给出一个两个字符串,求出最短且在两个字符串中唯一的公共子串. [题解] 以原字 ...
- 识别子串 (string)——后缀自动机+线段树
题目 [题目描述] 一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当: 1.$ i \leq x \leq j ...
随机推荐
- Java中通过Class类获取Class对象的方法详解
方式1:通过Object类的getObject()方法 Person p = new Person(); Class c = p.getClass(); 方式2: 通过 类名.class 获取到字节码 ...
- html网页什么样的字体最好看,css设置各种中文字体样式代码
css代码如下:{ font-family:"Microsoft YaHei",微软雅黑,"MicrosoftJhengHei",华文细黑,STHeiti,Mi ...
- (2)Python3笔记 数据类型之"组"(序列) 与 集合
一. list [ 列表 ] ---- 任意数据类型的有序排列 1. list 操作类似 str操作 (参考上一节字符串操作) [1,2,3,4] [1,2,'a', True, [2,'b']] ...
- webpack 解决跨域问题
一.webpack 内置了 http-proxy-middleware 可以解决 请求的 URL 代理的问题 安装:npm install --save http-proxy-middleware 二 ...
- python对缩进的严格要求
一般的编辑器都会默认Tab键为四格缩进,但是在python脚本中,Tab与直接4个tablespace空格是有区别的. 一般表现为报错信息如下: IndentationError: unindent ...
- Oralce安装、使用过程中出现的问题
OracleDBControl启动失败Unable to determine local host from URL REPOSITORY_URL=http://your-url.co 解决方法 打开 ...
- word中加入endnote
http://jingyan.baidu.com/article/f54ae2fc3926d91e92b849c2.html 1.如果安装完endnote后,word中没有出现 endnote菜单,则 ...
- Unity shader学习之阴影
Unity阴影采用的是 shadow map 的技术,即把摄像机放到光源位置上,看不到的地方就有阴影. 前向渲染中,若一光源开启了阴影,Unity会计算它的阴影映射纹理(shadow map),它其实 ...
- kali linux主题下载
主题下载网站 https://www.gnome-look.org/ 下载好安装包后解压 将文件夹移动到 usr/share/theme/ 下 mv download ../usr/share/the ...
- Cipher
Description Bob and Alice started to use a brand-new encoding scheme. Surprisingly it is not a Publi ...