题解 [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;
}
posted @ 2022-04-29 09:31  Administrator-09  阅读(4)  评论(0编辑  收藏  举报