题意:给出一个
2
×
n
2\times n
2×n的矩阵
A
A
A,每个元素均为小写字母。再给出一个字符串
s
s
s,问矩阵中有多少路径满足:
1.
1.
1. 路径上的字母连起来为
s
s
s。
2.
2.
2.不能多次经过同一位置。
3.
3.
3.只能向上下左右四个方向移动。
n , ∣ s ∣ ≤ 2000 n,|s|\leq 2000 n,∣s∣≤2000,答案对 1 e 9 + 7 1e9+7 1e9+7取模。
前置知识:哈希,动态规划。
对于在
2
×
n
2\times n
2×n的矩阵上的问题,一种常见的思路就是把一条路径拆分成多段,对每段分别求解再合并,有时需要用数据结构维护。那么对于这题,不难发现:任何一条路径都可以被划分成三段,如图所示:
1.
1.
1.蓝色部分:起点和起点左侧的部分。对于这一部分,考虑枚举起点。这一部分要么向左延伸后再回来,要么只包含起点一个点。考虑用
H
a
s
h
Hash
Hash判断这段路径是否合法,复杂度
O
(
n
2
)
O(n^2)
O(n2)
2.
2.
2.红色部分:起点右侧,终点左侧的部分。这部分一眼看上去不是很好求,考虑用
D
P
DP
DP计算答案。设
f
0
/
1
,
i
,
j
,
k
f_{0/1,i,j,k}
f0/1,i,j,k表示现在在
(
i
,
j
)
(i,j)
(i,j),匹配到字符串
s
s
s的第
k
k
k位,第一维为
0
0
0表示直接从上一列过来,没有经过
(
i
⊕
1
,
j
)
(i\oplus 1,j)
(i⊕1,j),第一维为
1
1
1表示经过
(
i
⊕
1
,
j
)
(i\oplus 1,j)
(i⊕1,j)到
(
i
,
j
)
(i,j)
(i,j)。
则有转移方程:
f
0
,
i
,
j
,
k
=
f
0
,
i
,
j
−
1
,
k
−
1
+
f
1
,
i
,
j
−
1
,
k
−
1
f_{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
(
A
i
,
j
=
s
k
)
(A_{i,j}=s_k)
(Ai,j=sk)
f
1
,
i
,
j
,
k
=
f
0
,
i
⊕
1
,
j
−
1
,
k
−
2
+
f
1
,
i
⊕
1
,
j
−
1
,
k
−
2
f_{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
(
A
i
,
j
=
s
k
,
A
i
⊕
1
,
j
=
s
k
−
1
)
(A_{i,j}=s_k,A_{i\oplus 1,j}=s_{k-1})
(Ai,j=sk,Ai⊕1,j=sk−1)
初始化就是通过枚举蓝色部分路径给
D
P
DP
DP赋初值。复杂度
O
(
n
2
)
O(n^2)
O(n2)
3.
3.
3.绿色部分:终点和终点右侧的部分。枚举路径和蓝色部分相似,若合法则将前面对应的
D
P
DP
DP值加入答案。
一些细节:
1.
1.
1.注意到起点可能在终点的右侧,要将
s
s
s翻转后再算一次。
2.
2.
2.可能三段路径中有两段都为空,而这些路径会被重复算一次,所以这部分应单独计算。
3.
3.
3.当
∣
s
∣
≤
2
|s|\leq 2
∣s∣≤2时,需要特判,这里不再赘述。
4.
4.
4.考虑到
C
F
CF
CF数据比较强,建议使用双哈希。
代码:
#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;
}