1. 程式人生 > 其它 >「SWTR-05」String

「SWTR-05」String

Description

給定串 \(S\)\(T\),每次可以在 \(T\) 前或後拼接一個 \(T\) 的子串,求最小步數使得 \(T\) 變成 \(S\)

Solution

暴力區間dp,剪剪枝有可以做到 50%,考場上沒判 -1,怒丟 40pts。

考慮一個區間 \([l,r]\) 能有的轉移,假設其能轉移到 \([l',r]\),其中 \(l'\in [x,l)\),那麼實際上我們只需要保留 \([x,l]\) 這個區間進行轉移,也就是每次找最遠的能轉移的區間進行轉移。考慮為什麼是對的。大區間一定是包含小區間的所有子串的,而選擇大區間還會使得剩下的串更小,不會使得答案更劣。那麼現在的問題就是快速處理出一個區間最多能擴充套件到哪兒。類比於上面的道理,記一個區間 \([l,r]\)

向右能擴充套件的最大長度為 \(f_{l,r}\),那麼一定有 \(f_{l,r}\geq f_{l+1,r}\),所以直接從 \(f_{l+1,r}\) 開始列舉,新增的能匹配的一定是以 \(l\) 為起點的串,可以用 Hash 擴充套件。

計算答案可以用 bfs,因為每次轉移增量都是一。

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

typedef long long ll;

const int Bs=233;
const int N=5e3+7;
const int Mod=998244353;

struct Seg{
    int l,r;
    Seg(int l_=0,int r_=0):l(l_),r(r_){}
};

queue<Seg> Q;
char s[N],t[N];
ll pw[N],hs=0,Hs[N];
int toL[N][N],toR[N][N],dp[N][N];

inline ll Get(int l,int r){
    return (Hs[r]-Hs[l-1]*pw[r-l+1]%Mod+Mod)%Mod;
}

int main(){
//    freopen("cutstring.in","r",stdin);
//    freopen("cutstring.out","w",stdout);
    scanf("%s%s",s+1,t+1);
    int n=strlen(s+1),m=strlen(t+1); pw[0]=1;
    for(int i=1;i<=n;i++)
        pw[i]=pw[i-1]*Bs%Mod,
        Hs[i]=(Hs[i-1]*Bs%Mod+s[i])%Mod;
    for(int i=1;i<=m;i++)
        hs=(hs*Bs%Mod+t[i])%Mod;
    for(int l=1;l<=n;l++){
        int ret=1;
        for(int r=l;r<=n;r++){
            while((l-ret>=1&&r-l+1>=ret)&&Get(r-ret+1,r)==Get(l-ret,l-1)) ret++;
            toL[l][r]=ret-1;
        }
    }
    for(int r=n;r;r--){
        int ret=1;
        for(int l=r;l;l--){
            while((r+ret<=n&&r-l+1>=ret)&&Get(l,l+ret-1)==Get(r+1,r+ret)) ret++;
            toR[l][r]=ret-1;
        }
    }
//    for(int i=1;i<=n;i++)
//        for(int j=i;j<=n;j++)
//            printf("%d %d %d %d\n",i,j,toL[i][j],toR[i][j]);
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=n;i++)
        if(Get(i,i+m-1)==hs) Q.push(Seg(i,i+m-1)),dp[i][i+m-1]=0;
    while(!Q.empty()){
        Seg t=Q.front(); Q.pop();
    //    printf("%d %d %d\n",t.l,t.r,dp[t.l][t.r]);
        if(toL[t.l][t.r]){
            const int del=toL[t.l][t.r];
            if(dp[t.l-del][t.r]==-1){
                dp[t.l-del][t.r]=dp[t.l][t.r]+1;
                Q.push(Seg(t.l-del,t.r));
            }
        }
        if(toR[t.l][t.r]){
            const int del=toR[t.l][t.r];
            if(dp[t.l][t.r+del]==-1){
                dp[t.l][t.r+del]=dp[t.l][t.r]+1;
                Q.push(Seg(t.l,t.r+del));
            }
        }
    }
    printf("%d",dp[1][n]);
}