题解: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;
}

浙公网安备 33010602011771号