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 ...
随机推荐
- Selenium基础知识(六)下拉列表定位
1.下拉列表定位 要选择下拉列表中的元素,要先定位到,下拉列表元素,然后可以通过xpath去点击,表内内容 例如,百度搜索-->百度设置-->搜索设置-->选择下拉列表框内" ...
- Linux SSH 免秘钥登录
SSH 免秘钥登录 ssh:是一种安全加密协议 ssh username@hostname ssh gongziyuan.com:以当前用户登录该机器(如果不是当前用户,需要这么干:ssh ...
- 集体干死java 在启动.sh
#!/bin/bash#reboot .jar#author wangdonghuipid=`ps -ef |grep java |awk '{print $2}'`echo $pidecho'--- ...
- storm 001
Hadoop.Storm系统和组件接口对比表: package storm; import org.apache.storm.Config; import org.apache.storm.Storm ...
- <9>cc.Sprite组件
1.精灵 精灵(Sprite)是Cocos系列的核心概念之一,是Cocos Creator最常用的显示图像的组件. 游戏中显示一个图片,我们就可以把这个叫做”精灵” sprite,这只是简单理解概念. ...
- kali linux wmtools安装
1,选择挂载盘时选择自动检测 2,点击安裝vmware tools安裝 3.tar -xzf 壓縮包名 4../vmware-install.pl 5,reboot
- ResourceExhaustedError 解决方案
原因:网络层太多,运算量太大导致GPU资源耗尽 解决方案: 1.限制GPU的使用: config = tf.ConfigProto()config.gpu_options.per_process_gp ...
- mysql主从配置,读写分离
Mysql主从配置,实现读写分离 大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够.到了数据业务层.数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库 ...
- html5-相对定位
*{ margin: 0px; padding: 0px;}div{ width: 300px; height: 300px;}#div1{ background: rg ...
- bash shell 编程练习
原始文件: find /etc -name passwd 2>&1 | tee ee.log 1. cat -n 把 e.log 的文档内容加上行号后输入 e2.log 这个文档里: x ...