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 ...
随机推荐
- C++ 类定义
C++ 类定义 定义一个类,本质上是定义一个数据类型的蓝图.这实际上并没有定义任何数据,但它定义了类的名称意味着什么,也就是说,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作. 类定义是 ...
- Linux基础(六) Vim之vundle插件
背景 Vim缺乏默认的插件管理器,所有插件的文件都散布在~/.vim下的几个文件夹中,插件的安装与更新与删除都需要自己手动来,既麻烦费事,又可能出现错误. Vundle简介 Vundle 是 Vim ...
- gitlab8.2->8.16->8.17->9.0升级
注:不要跨过8.16升级至8.17,这样做升级过程会报错 ###################################8.2升级至8.16########################## ...
- bat cmd 获取管理员权限
@ echo off % % ver|find "5.">nul&&goto :Admin mshta vbscript:createobject()(win ...
- Lambda表达式语法
基础语法:‘->’Lambda操作符* 左侧:Lambda表达式的参数列表 对应接口中方法中的参数列表中的参数(比如nice1中MyPredict这个接口中的方法)* 右侧:Lambda表达式中 ...
- vs2017创建支持多框架(net4.6.1;net4.6.2;netstandard2.0;netcoreapp2.0)版本
1.新建netcore或netstandard或net4.6.1项目 2.编辑项目文件: <Project Sdk="Microsoft.NET.Sdk"> < ...
- html5-table布局
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- web基础,用html元素制作web页面
用div,form制作登录页面,尽可能做得漂亮. 练习使用下拉列表选择框,无序列表,有序列表,定义列表. 观察常用网页的HTML元素,在实际的应用场景中,用已学的标签模仿制作. <!DOCTYP ...
- 【2017-2-23】C#switch case分支语句,for循环语句
switch case分支语句 switch(一个变量值) { case 值:要执行的代码段;break; case 值:要执行的代码段;break; … default:代码段;break;(def ...
- Second LearningConvolutionalNeuralNetworksforGraphs Experience
paper +ppt 链接:https://pan.baidu.com/s/1ZLBvv7mP8OoseQ4tnwhr_A 提取码:4amg