题解 [CF914F] Substrings in a String
bitset 是世界上最好的字符串匹配算法!
脑残想法:
保证了 \(\sum len\),那么不同的串长只有根号种
那么对每种长度分别做一遍,线段树上每个位置维护以这个位置为起点长为 len 的串的 hash 值
那么单点修改就是区间加等差数列了,这样可以维护出所有有用的 hash 值
然并卵
然后一种比正解快的做法:
发现字符集只有 26
于是可以 bitset 找到模式串在原串中的所有出现位置
移位处理一下就可以找到在目标区间中的出现次数了
这题输入的 l,r 还有 \(l>r\) 的情况是怎么回事呀
然后正解:
题目很根号分治,所以写分块
将原串分为 \(\sqrt n\) 块
询问串长 \(>\sqrt n\) 时,暴力 kmp
询问串长 \(\leqslant \sqrt n\) 时,暴力重构询问区间包含的块,暴力 kmp 处理边角
然后怎么统计跨越了两个块的那些串呢?
发现有 \(\sqrt n\) 个两个块的边界,每个边界只需要暴力匹配长为 len 的一段
所以最终复杂度是 \(O(n\sqrt n)\),听说没 bitset 跑的快
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
int n, q;
char s[N], t[N];
bitset<100010> ans, mask, pos[26];
signed main()
{
scanf("%s%d", s+1, &q);
n=strlen(s+1);
for (int i=1; i<=n; ++i) mask[i]=1;
for (int i=1; i<=n; ++i) pos[s[i]-'a'][i]=1;
for (int i=1,op,l,r,x,len; i<=q; ++i) {
scanf("%d", &op);
if (op&1) {
scanf("%d%s", &x, t);
pos[s[x]-'a'][x]=0;
pos[(s[x]=*t)-'a'][x]=1;
}
else {
scanf("%d%d%s", &l, &r, t);
len=strlen(t);
if (r-l+1<len) {puts("0"); continue;}
ans=mask;
for (int j=0; j<len; ++j) ans&=pos[t[j]-'a']>>j;
// cout<<"ans: "<<ans<<endl;
printf("%d\n", (int)((ans>>l).count()-(ans>>(r-len+2)).count()));
}
}
return 0;
}