Desciption
给定一个 (2 imes n) 的字符矩阵和一个串 (S),问 (S) 中有多少种匹配方式。
Solution
这题的突破口在于行数为二,那么这个匹配的路径就会呈现一定规律,从而方便 dp。具体而言,路径一定形如:
-
往前走 (a eq 1) 步,再掉头走 (a) 步回来。
-
扭动着往后走。
-
往后走 (b eq 1) 步,再掉头走 (b) 步回来。
用 (dp[i][j][k][0/1]) 表示当前在 ((i,j)) 已经匹配到了第 (k) 位,当前是从前另一边转移的方案数。
对于 (1) ,可以用 Hash 来与处理出一些 dp 值,对于二,有转移
[dp[i][j][k][0] +=dp[i][j-1][k-1][0/1] \
dp[i][j][k][1] +=dp[i ext{^}1][j][k-1][0]
]
最后再枚举 (b) ,结合 (dp) 计算答案。注意到路径可能是反着的,所以要把串翻过来再做一遍。对于 (n=1) 或 (n=2) 要去重。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
const int N=2e3+7;
const int Bs=233;
const int Mod=998244353;
const int mod=1e9+7;
ll pw[N];
struct Node{
char s[N]; int n;
ll Hs[2][N];
char operator [](int x){return s[x];}
inline void in(){
scanf("%s",s+1),n=strlen(s+1);
}
inline void rev(){
reverse(s+1,s+1+n); pre();
}
inline void pre(){
for(int i=1;i<=n;i++)
Hs[0][i]=(Hs[0][i-1]*Bs%Mod+s[i])%Mod;
for(int i=n;i;i--)
Hs[1][i]=(Hs[1][i+1]*Bs%Mod+s[i])%Mod;
}
inline ll Get(int l,int r,bool op=0){
if(!op) return (Hs[0][r]-pw[r-l+1]*Hs[0][l-1]%Mod+Mod)%Mod;
return (Hs[1][l]-pw[r-l+1]*Hs[1][r+1]%Mod+Mod)%Mod;
}
}A[2],B;
inline void pl(ll &x,const ll &y){
x+=y; if(x>=mod) x-=mod;
}
int n,m;
ll ans=0,dp[2][N][N][2];
void calc(){
memset(dp,0,sizeof(dp));
for(int i=0;i<2;i++)
for(int j=1;j<=n;j++){
dp[i][j][1][0]=(A[i][j]==B[1]);
for(int k=2,rg=min(m/2,j);k<=rg;k++)
dp[i][j][k<<1][1]=(A[i].Get(j-k+1,j,0)==B.Get(k+1,k<<1))
&(A[i^1].Get(j-k+1,j,1)==B.Get(1,k));
}
for(int k=1;k<=m;k++)
for(int i=0;i<2;i++)
for(int j=1;j<=n;j++){
if(A[i][j]!=B[k]) continue;
pl(dp[i][j][k][0],(dp[i][j-1][k-1][0]+dp[i][j-1][k-1][1])%mod);
pl(dp[i][j][k][1],dp[i^1][j][k-1][0]);
}
for(int i=0;i<2;i++)
for(int j=1;j<=n;j++)
for(int k=0;k<=m;k++){
int ret=m-k;
if((ret&1)||ret==2) continue; else ret>>=1;
if((m==k)||((j+ret<=n)&(A[i].Get(j+1,j+ret,0)==B.Get(k+1,k+ret))
&(A[i^1].Get(j+1,j+ret,1)==B.Get(m-ret+1,m))))
pl(ans,(dp[i][j][k][0]+dp[i][j][k][1])%mod);
}
}
int main(){
freopen("snake.in","r",stdin);
freopen("snake.out","w",stdout);
pw[0]=1; for(int i=1;i<N;i++) pw[i]=pw[i-1]*Bs%Mod;
A[0].in(),A[1].in(),B.in(); A[0].pre(),A[1].pre(),B.pre();
n=A[0].n,m=B.n;
calc(); if(m==1) return printf("%lld",ans),0;
A[0].rev(),A[1].rev(); calc();
if(m==2){
for(int i=0;i<2;i++)
for(int j=1;j<=n;j++)
ans-=(A[i][j]==B[1]&&A[i^1][j]==B[2]);
}
printf("%lld",ans);
}