1. 程式人生 > 實用技巧 >2020年HDU多校第二場 1012 String Distance(序列自動機,dp)

2020年HDU多校第二場 1012 String Distance(序列自動機,dp)

2020年HDU多校第二場 1012 String Distance(序列自動機,dp)

String Distance

題意:給兩個字串,第一個很長,第二個很短,q次詢問每次給一個l,r問操作多少次使第一串的l到r與第二串相等,每次操作選擇兩串其1在任意位置增加元素或刪除元素;

題解:首先很容易想到增加操作是沒有必要的,因為我可以在某字串增加必然可以在另一串相同位置刪除,所以很明顯就是求兩串的最長公共子序列,普通的最長公共子序列是寫不了的,因為他給的l的限制,我想預處理出所有dp的話,時間與空間都不夠,我們發現,第二個字串的長度特別短,所以可以用序列自動機對第一個字串進行降維打擊,然後每次詢問進行一次dp既可,O(20*20)。dp的話用記憶化搜尋會好寫很多,可惜HDU卡的太死了,搜尋寫法超時,這裡給上遞推式的寫法。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1e9+7;
int m,len,t,n,q,g[100007][30],dp[27][27],l,r;
char s1[100007],s2[100007];
int solve(){
    for(int i=0;i<=m;i++){
        for(int j=0;j<=m;j++){
            dp[i][j]=100006;
        }
    }
    for(int i=0;i<=m;i++)dp[i][0]=l;
    int ans=0;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=i;j++){
            dp[i][j]=dp[i-1][j];
            dp[i][j]=min(dp[i][j],g[dp[i-1][j-1]][s2[i]-'a']+1);
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=i;j++){
            if(dp[i][j]<=r+1){
                ans=max(ans,j);
            }
        }
    }
    return ans;
}
void init(){
    memset(g,maxn,sizeof(g));
}
int main(){
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%s%s",s1+1,s2+1);
        len=strlen(s1+1);
        m=strlen(s2+1);
        for(int i=0;i<=25;i++)g[len+1][i]=1e9;
        for(int i=len;i>=1;i--){
            for(int j=0;j<=25;j++){
                g[i][j]=g[i+1][j];
            }
            g[i][s1[i]-'a']=i;
        }
        scanf("%d",&q);
        while(q--){
            scanf("%d%d",&l,&r);
            printf("%d\n",m+r-l+1-2*solve());
        }
    }
}