题解:P3618 误会

题目传送门

思路分析

为简便描述,下文中 \(s1\) 是第一个字符串,\(s2\) 是第二个字符串。

发现我们需要替换相同句子部分,所以我们必须知道哪几个位置相同,于是就想到哈希来判断。先求出 \(s2\) 的哈希值,然后再逐位求 \(s1\) 中每 \(|s2|\) 个长度的字符串的哈希值,可以用前缀和来求,注意要对齐位置。下面有注释。

void init(){
	s1=" "+s1,s2=" "+s2;hss=0;
	for(int i=1;i<s2.size();i++) hss=(hss*P)+(int)(s2[i]-'a'+1); 
	for(int i=1;i<s1.size();i++){
		mk[i]=false;
		hs[i]=(hs[i-1]*P)+(int)(s1[i]-'a'+1);
		//逐位计算s1的哈希值 
		if(i>=s2.size()-1){
			int x=hs[i]-hs[i+1-s2.size()]*ksm(P,(s2.size()-1));
			//ksm是求P^s2.size的,为了对齐数位
			//原来是:   aaa
			//现在是:bbbaaa
			//为了对齐数位,将原来的aaa*ksm(P,s1.size()) 
			if(x==hss) mk[i]=true;
			//如果和s2的哈希值一样,记录下来 
		}
	}
}

现在我们已经知道了哪些位置能够匹配上,接下来考虑有几种可能。考虑 DP,设 \(dp_i\) 表示截止到第 \(i\) 位有 \(dp_i\) 种答案。

  • 我们发现如果当前位可以匹配,那么第 \(i\) 位的答案可以从第 \(i-|s2|\) 的地方转移,因为我们可以选择当前位替不替换为 *,如果不替换就是 \(dp_{i-1}\),如果替换的话就是 \(dp_{i-|s2|}\),所以 \(dp_i=dp_{i-1}+dp_{i-|s2|}\)

  • 如果当前位不能匹配,那么第 \(i\) 位的答案就是第 \(i-1\) 位的答案,即 \(dp_i=dp_{i-1}\)

注意,如果全部都不换为 *,那么也算一种方案,所以我们令 \(dp_0=1\)。注意取模。

代码如下:

void solve(int x){
	dp[0]=1;
	//设置dp初始值 
	for(int i=1;i<s1.size();i++){
		dp[i]=dp[i-1];
		//如果不匹配就直接转移 
		if(mk[i]) dp[i]=(dp[i]+dp[i-s2.size()+1])%mod;
		//如果匹配就再加上dp[i-s2.size]
		//注意取模 
	}
	cout<<"Case #"<<x<<": "<<dp[(s1.size()-1)]<<"\n";
}

AcCode

#include <bits/stdc++.h>
using namespace std;
#define int unsigned long long

const int N=1e5+10; 
const int P=131;
const int mod=1e9+7;

int n,hss;
int hs[N],dp[N];

string s1,s2;

bool mk[N];

inline int read(){
	int t=0,f=1;
	register char c=getchar();
	while(c<'0'||c>'9') f=(c=='-')?(-1):(f),c=getchar();
	while(c>='0'&&c<='9') t=(t<<3)+(t<<1)+(c^48),c=getchar();
	return t*f;
}

int ksm(int x,int y){
	int sum=1;
	while(y){
		if(y&1) sum*=x;
		x*=x;
		y>>=1;
	}
	return sum;
}

void init(){
	s1=" "+s1,s2=" "+s2;hss=0;
	for(int i=1;i<s2.size();i++) hss=(hss*P)+(int)(s2[i]-'a'+1); 
	for(int i=1;i<s1.size();i++){
		mk[i]=false;
		hs[i]=(hs[i-1]*P)+(int)(s1[i]-'a'+1);
		if(i>=s2.size()-1){
			int x=hs[i]-hs[i+1-s2.size()]*ksm(P,(s2.size()-1));
			if(x==hss) mk[i]=true;
		}
	}
}

void solve(int x){
	dp[0]=1;
	for(int i=1;i<s1.size();i++){
		dp[i]=dp[i-1];
		if(mk[i]) dp[i]=(dp[i]+dp[i-s2.size()+1])%mod;
	}
	cout<<"Case #"<<x<<": "<<dp[(s1.size()-1)]<<"\n";
}

signed main(){
	int T=read();
	for(int i=1;i<=T;i++){
		cin>>s1>>s2;
		init();
		solve(i);
	}
	return 0;
}
posted @ 2025-03-29 10:54  ask_silently  阅读(26)  评论(0)    收藏  举报