hdu6774 String Distance(序列自动机+dp)

题意:

给定长度为n的串s,长度为m的串t,
一次操作,你可以删除s串或者t串一个字符或者在任意位置插入一个字符
q次询问,每次询问给出L,R,问s[L,R]和t至少操作几次才能变得一样

数据范围:n<=1e5,m<=20,q<=1e5

解法:

s串数据范围比较大,询问也比较多,但是观察到m比较小,从m入手
发现插入和删除操作可以在s串也可以在t串上进行,那么其实只进行删除就行了
计算最小删除次数可以转化为计算最大暴力次数,最大保留次数就是两个串的最大公共子序列长度
设最大公共子序列长度为ma,那么答案就是r-l+1-ma*2

令d(i,j)表示t串前i位在s串上匹配j位时的最小下标,
d(i,j)可以从d(i,j-1)和d(i-1,j-1)转移而来
从d(i-1,j-1)转移需要找s串d(i-1,j-1)位置后面第一个s(i),可以用序列自动机进行快速计算

code:

#include
using namespace std;
const int maxm=1e5+5;
char s[maxm],t[25];
int nt[maxm][26];
int d[25][25];
int n,m;
signed main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%s%s",s+1,t+1);
        n=strlen(s+1),m=strlen(t+1);
        //预处理部分
        for(int j=0;j<26;j++){
            nt[n][j]=0;
        }
        for(int i=n;i>=1;i--){
            for(int j=0;j<26;j++){
                nt[i-1][j]=nt[i][j];
            }
            nt[i-1][s[i]-'a']=i;
        }
        //
        int q;scanf("%d",&q);
        while(q--){
            int l,r;scanf("%d%d",&l,&r);
            for(int i=0;i<=m;i++){
                for(int j=0;j<=m;j++){
                    d[i][j]=n+1;
                }
            }
            int ma=0;
            d[0][0]=l-1;
            for(int i=1;i<=m;i++){//d[i][j]表示t串前i个匹配j个的最小位置
                d[i][0]=l-1;
                for(int j=1;j<=i;j++){
                    if(j<=i-1)d[i][j]=min(d[i][j],d[i-1][j]);//从d[i-1][j]转移
                    if(nt[d[i-1][j-1]][t[i]-'a']&&nt[d[i-1][j-1]][t[i]-'a']<=r){//从d[i-1][j-1]转移
                        d[i][j]=min(d[i][j],nt[d[i-1][j-1]][t[i]-'a']);
                    }
                    if(d[i][j]<=r)ma=max(ma,j);
                }
            }
            int ans=r-l+1+m-2*ma;
            printf("%d\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(hdu6774 String Distance(序列自动机+dp))