【CCPC2023深圳】相似基因序列问题

2023-12-13 04:25:22

题目传送门

https://cpc.csgrandeur.cn/csgoj/problemset/problem?pid=1243

思路

K=0

对于每个字符串求出它的哈希值, O ( n q ) O(nq) O(nq) 两两比对就好

K=1

这是一个经典的二分+哈希问题。

考虑这样两个字符串
s 1 = a b c a b c a b c a b c s1=abcabcabcabc s1=abcabcabcabc
s 2 = a b c a b a a b c a b c s2=abcabaabcabc s2=abcabaabcabc
分别令 p r e 1 [ i ] pre1[i] pre1[i] p r e 2 [ i ] pre2[i] pre2[i] s 1 , s 2 s1,s2 s1,s2的前缀
可以发现当 i ≤ 5 i≤5 i5时, p r e 1 [ i ] = p r e 2 [ i ] pre1[i]=pre2[i] pre1[i]=pre2[i]
但是当 i > 5 i>5 i>5时, p r e 1 [ i ] ≠ p r e 2 [ i ] pre1[i]≠pre2[i] pre1[i]=pre2[i]
也就说, i i i越小,前缀越容易相等,当 i i i大于某个值,前缀就不再相等了。
也就说最大的 i i i具有可二分性
于是我们二分 i m a x i_{max} imax?,用哈希判断二分出的前缀是否相等。
那么 s 1 , s 2 s1, s2 s1,s2会在 i m a x + 1 i_{max}+1 imax?+1不相同
之后判断 i m a x + 2 i_{max}+2 imax?+2后面的部分是否相等即可。
时间复杂度 O ( n q l o g m ) O(nqlogm) O(nqlogm)

K>1

考虑这样一组数据
K = 2 K=2 K=2
s 1 = a a b a a c a a s1=aabaacaa s1=aabaacaa
s 2 = a a a a c c a a s2=aaaaccaa s2=aaaaccaa
按照 K = 1 K=1 K=1的做法,我们可以二分出 i m a x = 2 i_{max}=2 imax?=2
于是 s 1 , s 2 s1,s2 s1,s2 i = 3 i=3 i=3的位置失配(不一样)了。可以理解为消耗了一次失配的机会。

显然,前面的东西我们已经考虑过了,对后面的答案不会有影响了,所以这组数据等价于
K = 1 K=1 K=1
s 1 ′ = a a c a a s1'=aacaa s1=aacaa
s 2 ′ = a c c a a s2'=accaa s2=accaa
于是又回到了 K = 1 K=1 K=1的情况

可以发现,一个失配的位置就会把字符串分成两段,同时消耗一次失配的机会(K-1)
那么 K K K个失配的位置就会吧字符串分成 K + 1 K+1 K+1
所以我们进行 K + 1 K+1 K+1次二分,判断最后一次的 i m a x i_{max} imax?是否等于 m m m即可
时间复杂度 O ( n q K l o g m ) O(nqKlogm) O(nqKlogm)

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=307,M=60007,p=19260817,mod=1e9+7;
int a[N];
vector<int> hs[N];
vector<int> h,_p;
int m;
bool Hash(int l,int r,int i)
{
	int hs1=(hs[i][r]-hs[i][l-1]+mod)*_p[m-r]%mod;
	int hs2=(h[r]-h[l-1]+mod)*_p[m-r]%mod;
	return hs1==hs2;
}
void init(int m)
{
	_p.resize(m+1);
	_p[0]=1;
	for(int i=1; i<=m; i++) _p[i]=_p[i-1]*p%mod;
}
void O_o()
{
	int n,q,k;
	string s;
	cin>>n>>q>>m>>k;
	init(m);
	for(int i=1; i<=n; i++)
	{
		cin>>s;
		int j=0;
		hs[i].resize(m+1);
		for(auto c:s)
		{
			j++;
			hs[i][j]=(hs[i][j-1]+c*_p[j-1])%mod;
		}
	}
	while(q--)
	{
		int ans=0;
		cin>>s;
		h.resize(m+1);
		int j=0;
		for(auto c:s)
		{
			j++;
			h[j]=(h[j-1]+c*_p[j-1])%mod;
		}
		for(int i=1; i<=n; i++)
		{
			int t=k;
			bool bz=0;
			int las=0;
			while(t>=0&&!bz)
			{
				int l=las+1,r=m,ass=m+1;
				while(l<=r)
				{
					int mid=l+r>>1;
					if(Hash(l,mid,i))
					{
						l=mid+1;
					}
					else
					{
						ass=mid;
						r=mid-1;
					}
				}
				if(ass==m+1)
				{
					bz=1;
					break;
				}
				las=ass;
				t--;
			}
			ans+=bz;
		}
		cout<<ans<<"\n";
	}
}
signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cout<<fixed<<setprecision(2);
	int T=1;
//	cin>>T;
	while(T--)
	{
		O_o();
	}
}


文章来源:https://blog.csdn.net/Eric1561759334/article/details/134959564
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。