[机房测试]蛇

Desciption

给定一个 (2 imes n) 的字符矩阵和一个串 (S),问 (S) 中有多少种匹配方式。

Solution

这题的突破口在于行数为二,那么这个匹配的路径就会呈现一定规律,从而方便 dp。具体而言,路径一定形如:

  1. 往前走 (a eq 1) 步,再掉头走 (a) 步回来。

  2. 扭动着往后走。

  3. 往后走 (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);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/15355155.html