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 ...
随机推荐
- Sklearn的使用
初步接触要求时,从上图选自己数据所适用的方法, 首先看数据的样本是否 >50,小于则需要收集更多的数据 然后看问题适合分类.回归.聚类.降维中的哪一大类 Sklearn解决问题的一般步骤: 1. ...
- (3)Python3笔记之变量与运算符
一.变量 1). 命名规则: 1. 变量名不能使用系统关键字或保留关键字 2. 变量区分大小写 3. 变量命名由字母,数字,下划线组成但不能以数字开头 4. 不需要声明变量类型 是 a = 1 ...
- Unity shader学习之逐像素漫反射光照模型
shader如下: Shader "Custom/Diffuse Fragment-Level" { Properties { _Diffuse (,,,) } SubShader ...
- 鼠标移动上去,元素旋转;web前端鼠标经过图片凸起
.trans-rotate{ -webkit-transition: transform .25s linear; -moz-transition: transform .25s linear; -o ...
- 记一次CentOS5.7更新glibc导致libc.so.6失效,系统无法启动
以下是错误示范,错误过程还原,请勿模仿!!! wkhtmltopdf 启动,提示/lib64/libc.so.6版本过低 $ ./wkhtmltopdf http:www.baidu.com 1. ...
- Spring源码阅读(三)
上一讲我们谈到单例生产关键方法getSingleton.getSingleton方法由DefaultSingletonBeanRegistry类实现.我们的抽象工厂AbstractBeanFactor ...
- window下nodejs用nodemon启动koa2项目(用cmd启动不了,要用Git Bash Here 启动才可以)
window下nodejs用nodemon启动koa2项目(用cmd启动不了,要用Git Bash Here 启动才可以)nodemon --watch 'app/**/*' -e ts --exec ...
- AELF(ELF)区块链项目介绍
AELF(ELF)区块链项目介绍,Aelf在交易所上的名称是ELF,最近涨了不少了,可以长期关注逢低建仓,根据自身情况可以适当轻仓配置点.AELF总结下来就是希望打造一个B2B的区块链开放式OS系统. ...
- Promise的简单用法
众所周知的,Javascript是一种单线程的语言,所有的代码必须按照所谓的“自上而下”的顺序来执行.本特性带来的问题就是,一些将来的.未知的操作,必须异步实现.本文将讨论一个比较常见的异步解决方案— ...
- HDU 2511 汉诺塔X
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2511 1,2,...,n表示n个盘子.数字大盘子就大.n个盘子放在第1根柱子上.大盘不能放在小盘上.在 ...