题意:给出一个2×n2\times n2×n的矩阵AAA,每个元素均为小写字母。再给出一个字符串sss,问矩阵中有多少路径满足:
1.1.1. 路径上的字母连起来为sss。
2.2.2.不能多次经过同一位置。
3.3.3.只能向上下左右四个方向移动。
n,∣s∣≤2000n,|s|\leq 2000n,∣s∣≤2000,答案对1e9+71e9+71e9+7取模。
前置知识:哈希,动态规划。
对于在2×n2\times n2×n的矩阵上的问题,一种常见的思路就是把一条路径拆分成多段,对每段分别求解再合并,有时需要用数据结构维护。那么对于这题,不难发现:任何一条路径都可以被划分成三段,如图所示:
1.1.1.蓝色部分:起点和起点左侧的部分。对于这一部分,考虑枚举起点。这一部分要么向左延伸后再回来,要么只包含起点一个点。考虑用HashHashHash判断这段路径是否合法,复杂度O(n2)O(n^2)O(n2)
2.2.2.红色部分:起点右侧,终点左侧的部分。这部分一眼看上去不是很好求,考虑用DPDPDP计算答案。设f0/1,i,j,kf_{0/1,i,j,k}f0/1,i,j,k表示现在在(i,j)(i,j)(i,j),匹配到字符串sss的第kkk位,第一维为000表示直接从上一列过来,没有经过(i⊕1,j)(i\oplus 1,j)(i⊕1,j),第一维为111表示经过(i⊕1,j)(i\oplus 1,j)(i⊕1,j)到(i,j)(i,j)(i,j)。
则有转移方程:
f0,i,j,k=f0,i,j−1,k−1+f1,i,j−1,k−1f_{0,i,j,k}=f_{0,i,j-1,k-1}+f_{1,i,j-1,k-1}f0,i,j,k=f0,i,j−1,k−1+f1,i,j−1,k−1 (Ai,j=sk)(A_{i,j}=s_k)(Ai,j=sk)
f1,i,j,k=f0,i⊕1,j−1,k−2+f1,i⊕1,j−1,k−2f_{1,i,j,k}=f_{0,i\oplus 1,j-1,k-2}+f_{1,i\oplus 1,j-1,k-2}f1,i,j,k=f0,i⊕1,j−1,k−2+f1,i⊕1,j−1,k−2 (Ai,j=sk,Ai⊕1,j=sk−1)(A_{i,j}=s_k,A_{i\oplus 1,j}=s_{k-1})(Ai,j=sk,Ai⊕1,j=sk−1)
初始化就是通过枚举蓝色部分路径给DPDPDP赋初值。复杂度O(n2)O(n^2)O(n2)
3.3.3.绿色部分:终点和终点右侧的部分。枚举路径和蓝色部分相似,若合法则将前面对应的DPDPDP值加入答案。
一些细节:
1.1.1.注意到起点可能在终点的右侧,要将sss翻转后再算一次。
2.2.2.可能三段路径中有两段都为空,而这些路径会被重复算一次,所以这部分应单独计算。
3.3.3.当∣s∣≤2|s|\leq 2∣s∣≤2时,需要特判,这里不再赘述。
4.4.4.考虑到CFCFCF数据比较强,建议使用双哈希。
代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#define gc getchar
#define pc putchar
#define pb push_back
#define f first
#define s second
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
template<class Typ> Typ &Rd(Typ &x){
char ch=gc(),sgn=0; x=0;
for(;ch<'0'||ch>'9';ch=gc()) sgn|=ch=='-';
for(;ch>='0'&&ch<='9';ch=gc()) x=x*10+(ch^48);
return sgn&&(x=-x),x;
}
template<class Typ> void Wt(Typ x){
if(x<0) pc('-'),x=-x;
if(x>9) Wt(x/10);
pc(x%10^48);
}
const int N=2e3+5;
const int Md=1e9+7;
const LL B1=131,B2=237;
const LL M1=998244353,M2=1000000123;
struct HASH{LL H1,H2;}H1[2][N],H2[2][N],H[N];
int n,F[2][2][N][N],ans,len,cur;
LL P1[N],P2[N]; char s[N],A[2][N];
bool operator ==(HASH A,HASH B){return A.H1==B.H1&&A.H2==B.H2;}
HASH operator+(HASH A,HASH B){return {(A.H1+B.H1)%M1,(A.H2+B.H2)%M2};}
HASH operator-(HASH A,HASH B){return {(A.H1-B.H1+M1)%M1,(A.H2-B.H2+M2)%M2};}
HASH operator*(HASH A,HASH B){return (HASH){A.H1*B.H1%M1,A.H2*B.H2%M2};}
HASH GET1(int op,int l,int r){return H1[op][r]-H1[op][l-1]*(HASH){P1[r-l+1],P2[r-l+1]};}
HASH GET2(int op,int l,int r){return H2[op][l]-H2[op][r+1]*(HASH){P1[r-l+1],P2[r-l+1]};}
HASH GET(int l,int r){return H[r]-H[l-1]*(HASH){P1[r-l+1],P2[r-l+1]};}
void SPJ(){
for(int i=0;i<=1;i++)
for(int j=1;j<=n;j++)
ans+=A[i][j]==s[1];
Wt(ans),exit(0);
}
void GETANS(){
memset(F,0,sizeof(F));
for(int i=1;i<=len;i++)
H[i]=H[i-1]*(HASH){B1,B2}\
+(HASH){s[i]-'a'+1,s[i]-'a'+1};
for(int i=0;i<=1;i++) for(int j=1;j<=n;j++)
for(int k=1;k<=j&&2*k<=len;k++)
if(GET2(i,j-k+1,j)*(HASH){P1[k],P2[k]}\
+GET1(i^1,j-k+1,j)==GET(1,2*k))
F[1][i^1][j][2*k]++,(2*k==len)&&cur++;
for(int i=0;i<=1;i++) for(int j=1;j<=n;j++)
if(A[i][j]==s[1]) F[0][i][j][1]++;
for(int k=1;k<=len;k++) for(int j=1;j<=n;j++)
for(int i=0;i<=1;i++) if(A[i][j]==s[k]){
(F[0][i][j][k]+=F[0][i][j-1][k-1])%=Md;
(F[0][i][j][k]+=F[1][i][j-1][k-1])%=Md;
if(k>1&&A[i^1][j]==s[k-1]){
(F[1][i][j][k]+=F[0][i^1][j-1][k-2])%=Md;
(F[1][i][j][k]+=F[1][i^1][j-1][k-2])%=Md;
}
}
for(int i=0;i<=1;i++) for(int j=1;j<=n;j++)
for(int k=1;k<=n-j+1&&2*k<=len;k++)
if(GET1(i^1,j,j+k-1)*(HASH){P1[k],P2[k]}\
+GET2(i,j,j+k-1)==GET(len-2*k+1,len)){
(ans+=(F[0][i^1][j-1][len-2*k]+\
F[1][i^1][j-1][len-2*k])%Md)%=Md;
if(2*k==len&&len>2) cur++;
}
for(int i=0;i<=1;i++) for(int j=1;j<=n;j++)
if(A[i][j]==s[len]) (ans+=(F[0][i][j-1][len-1]+\
F[1][i][j-1][len-1])%Md)%=Md;
}
int main(){
P1[0]=P2[0]=1;
for(int i=1;i<N;i++) P1[i]=P1[i-1]*B1%M1;
for(int i=1;i<N;i++) P2[i]=P2[i-1]*B2%M2;
scanf("%s%s%s",A[0]+1,A[1]+1,s+1);
n=strlen(A[0]+1),len=strlen(s+1); if(len==1) SPJ();
for(int i=0;i<=1;i++) for(int j=1;j<=n;j++)
H1[i][j]=H1[i][j-1]*(HASH){B1,B2}\
+(HASH){A[i][j]-'a'+1,A[i][j]-'a'+1};
for(int i=0;i<=1;i++) for(int j=n;j>=1;j--)
H2[i][j]=H2[i][j+1]*(HASH){B1,B2}\
+(HASH){A[i][j]-'a'+1,A[i][j]-'a'+1};
GETANS(),reverse(s+1,s+len+1),GETANS(),Wt((ans+cur/2)%Md);
return 0;
}