神级
D
P
DP
DP。把一条匹配路径分为左中右三段,左边和右边都是往一个方向走一段然后返回,而中间就是往一个方向蛇皮走位。
于是串分两个方向两次
D
P
DP
DP。
注意
D
P
DP
DP的细节判断,以及哈希区间的左右方向。对于
m
m
m较小的情况可以直接算。记得要双哈希,我太难了。
#include<bits/stdc++.h>
using namespace std;
#define puu pair<int,int>
#define mp make_pair
const int maxn=2e3+10,mod=1e9+7;
const int base1=37,base2=131;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void Add(int &x,int y){x=(x+y)%mod;}
int p1[maxn],p2[maxn];int n,m,ans=0,f[2][maxn][maxn];
struct str{
int pre1[maxn],suf1[maxn];
int pre2[maxn],suf2[maxn];
char s[maxn];int len;
inline void read(){
scanf("%s",s+1),len=strlen(s+1);
for(int i=1;i<=len;++i){
pre1[i]=add(s[i]-'a'+1,mul(pre1[i-1],base1));
pre2[i]=add(s[i]-'a'+1,mul(pre2[i-1],base2));
}
for(int i=len;i>=1;--i){
suf1[i]=add(s[i]-'a'+1,mul(base1,suf1[i+1]));
suf2[i]=add(s[i]-'a'+1,mul(base2,suf2[i+1]));
}
}
inline puu Hash(int l,int r){
if(l<=r) return mp(dec(pre1[r],mul(pre1[l-1],p1[r-l+1])),dec(pre2[r],mul(pre2[l-1],p2[r-l+1])));
return mp(dec(suf1[r],mul(suf1[l+1],p1[l-r+1])),dec(suf2[r],mul(suf2[l+1],p2[l-r+1])));
}
}s1,s2,w;
inline void prework(){
p1[0]=p2[0]=1;
for(int i=1;i<maxn;++i) p1[i]=mul(p1[i-1],base1),p2[i]=mul(p2[i-1],base2);
s1.read(),s2.read(),w.read();
}
inline void solve(int M,int ret=0){
if(M==1){
for(int i=1;i<=n;++i){
if(s1.s[i]==w.s[1]) ++ret;
if(s2.s[i]==w.s[1]) ++ret;
}
}
if(M==2){
for(int i=1;i<=n;++i){
if(s1.s[i]==w.s[1]&&s2.s[i]==w.s[2]) ++ret;
if(s2.s[i]==w.s[1]&&s1.s[i]==w.s[2]) ++ret;
}
for(int i=1;i<n;++i){
if(s1.s[i]==w.s[1]&&s1.s[i+1]==w.s[2]) ++ret;
if(s1.s[i+1]==w.s[1]&&s1.s[i]==w.s[2]) ++ret;
if(s2.s[i]==w.s[1]&&s2.s[i+1]==w.s[2]) ++ret;
if(s2.s[i+1]==w.s[1]&&s2.s[i]==w.s[2]) ++ret;
}
}printf("%d\n",ret);
}
inline bool operator==(const puu &a,const puu &b){return a.first==b.first&&a.second==b.second;}
int main(){
//freopen("string.in","r",stdin);
prework(),n=s1.len,m=w.len;
if(m<=2) return solve(m),0;
f[0][n+1][0]=f[1][n+1][0]=1;
for(int i=n;i>=1;--i){
f[0][i][0]=f[1][i][0]=1;
for(int k=2;(k+k<=m)&&(i+k-1<=n);++k){
puu t1=s1.Hash(i,i+k-1),t2=s2.Hash(i,i+k-1),t3=w.Hash(m,m-k+1),t4=w.Hash(m-k-k+1,m-k);
if (t1==t3&&t2==t4) f[1][i][k+k]=1;
if (t2==t3&&t1==t4) f[0][i][k+k]=1;
}
}
for(int i=n;i>=1;--i){
for(int k=1;k<=m;++k){
if(s1.s[i]==w.s[m-k+1]) Add(f[0][i][k],f[0][i+1][k-1]);
if(s2.s[i]==w.s[m-k+1]) Add(f[1][i][k],f[1][i+1][k-1]);
if(k>1&&s1.s[i]==w.s[m-k+1]&&s2.s[i]==w.s[m-k+2]) Add(f[0][i][k],f[1][i+1][k-2]);
if(k>1&&s2.s[i]==w.s[m-k+1]&&s1.s[i]==w.s[m-k+2]) Add(f[1][i][k],f[0][i+1][k-2]);
}
}
for(int i=1;i<=n+1;++i){
Add(ans,add(f[0][i][m],f[1][i][m]));
for(int k=2;(k+k<=m)&&(k<i);++k){
puu t1=s1.Hash(i-k,i-1),t2=s2.Hash(i-k,i-1),t3=w.Hash(k,1),t4=w.Hash(k+1,k+k);
if (t1==t3&&t2==t4) Add(ans,f[1][i][m-k-k]);
if (t2==t3&&t1==t4) Add(ans,f[0][i][m-k-k]);
}
}
memset(f,0,sizeof f),f[0][n+1][0]=f[1][n+1][0]=1;
for(int i=n;i>=1;--i){
f[0][i][0]=f[1][i][0]=1;
for(int k=2;(k+k<m)&&(i+k-1<=n);++k){
puu t1=s1.Hash(i,i+k-1),t2=s2.Hash(i,i+k-1),t3=w.Hash(1,k),t4=w.Hash(k+k,k+1);
if(t1==t3&&t2==t4) f[1][i][k+k]=1;
if(t1==t4&&t2==t3) f[0][i][k+k]=1;
}
}
for(int i=n;i>=1;--i){
for(int k=1;k<=m;++k){
if (s1.s[i]==w.s[k]) Add(f[0][i][k],f[0][i+1][k-1]);
if (s2.s[i]==w.s[k]) Add(f[1][i][k],f[1][i+1][k-1]);
if (k>1&&s1.s[i]==w.s[k]&&s2.s[i]==w.s[k-1]) Add(f[0][i][k],f[1][i+1][k-2]);
if (k>1&&s2.s[i]==w.s[k]&&s1.s[i]==w.s[k-1]) Add(f[1][i][k],f[0][i+1][k-2]);
}
}
for(int i=1;i<=n+1;++i){
Add(ans,add(f[0][i][m],f[1][i][m]));
for(int k=2;(k+k<m)&&(k<i);++k){
puu t1=s1.Hash(i-k,i-1),t2=s2.Hash(i-k,i-1),t3=w.Hash(m-k+1,m),t4=w.Hash(m-k,m-k-k+1);
if(t1==t3&&t2==t4) Add(ans,f[1][i][m-k-k]);
if(t1==t4&&t2==t3) Add(ans,f[0][i][m-k-k]);
}
}printf("%d\n",ans);
}
本文介绍了一道名为CF613E的算法题目解决方案,通过动态规划的方法解决字符串匹配问题,包括双哈希技巧及复杂度分析。

1012

被折叠的 条评论
为什么被折叠?



