字符串K次失配匹配

如果在正常的字符串匹配的基础上,如果我们加入可以有 \(K\) 个地方不一样该怎么做?

有一道这个问题的弱化,这篇文章记录一下这道题:P3763 [TJOI2017] DNA

这到题是允许有 3 个以下地方不一样,但不影响这种问题的思路。

就是正常的二分哈希。

我们记录两个串的哈希,到时候查找区间的哈希直接求就行了。

枚举每一个起始位置进行二分到最后一个匹配的位置,判断是否要使用机会。

这个是 \(O(nklogn)\) 的。

代码↓

点击查看代码
#include <bits/stdc++.h>
#define ull unsigned long long
using namespace std;
namespace BaiBaiShaFeng{
	const int MN=1e5+115;
	const int P=113;
	string s_0, s;
	int n, m, ans=0;
	ull hashed0[MN], p[MN], h[MN];
	void Read(){
		cin>>s_0>>s; p[0]=1; ans=0;
		n=s_0.size(); m=s.size();
		for(int i=1; i<=n; ++i) p[i]=(p[i-1]*P);
		s_0=' '+s_0; s=' '+s;
	}
	void Pre(){
		for(int i=1; i<=n; ++i)
			hashed0[i]=hashed0[i-1]*P+(s_0[i]-'A');
		for(int i=1; i<=m; ++i)
			h[i]=h[i-1]*P+(s[i]-'A');
	}
	int get0(int l, int r){
		return hashed0[r]-hashed0[l-1]*p[r-l+1];
	}
	int get1(int l, int r){
		return h[r]-h[l-1]*p[r-l+1];
	}
	bool Check(int s0l, int s0r, int sl, int sr){
		if(s0l>n||s0r>n||sl>m||sr>m) return false;
		return get0(s0l, s0r)==get1(sl,sr);
	}
	void solve(){
		Read(); Pre();
		for(int st=1; st<=n-m+1; ++st){
			int pos=1;//当前匹配的位置
			int pos0=st;
			int cnt=0, valid=false;
			while(pos<=m){
				if(pos0>n){
					valid=true;
					break;
				}
				int l=0, r=min(m-pos+1,n-pos0+1), res=0;
				while(l<=r){
					int mid=(l+r)>>1;
					if(Check(pos0,pos0+mid-1,pos,pos+mid-1)){
						res=mid; l=mid+1;
					}else{
						r=mid-1;
					}
				}
				pos+=res; pos0+=res;
				if(pos<=m){
					cnt++;
					if(cnt>3){
						valid=true;
						break;
					}
					pos++; pos0++;
				}
			}
			if(!valid&&cnt<=3){
				ans++;
			}
		}
		cout<<ans<<'\n';
	}
}
int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T=1; cin>>T; 
	while(T--){
		BaiBaiShaFeng::solve();
	}
	return 0;
}
posted @ 2025-10-16 11:07  BaiBaiShaFeng  阅读(5)  评论(0)    收藏  举报
Sakana Widget右下角定位