2020HDU多校第二场 HDU 6774 String Distance(DP+序列自动机)

题意:给两个字符串一个S串,一个T串。
每次查询,在S[L,L+1…R] 和T[1,2,…m] ( m为T的长度)中进行操作。
操作一:可以在任意一个字符串的任意一个位置加任意一个字符
操作二:可以在删除任意一个字符串内的任意一个字符。
问最少要操作多少次,能让两个字符串相等。

思路
1、容易发现,操作一的效果没啥用,效果等价操作二。
对一个字符串的一个位置加上字符,显然是想要让这个位置与另一个字符串该位置的字符一样。那么我直接把另一个字符串的这个位置的字符删掉即可。
那么问题等价找S[L,L+1…R] 和T[1,2,…m] 的LCS(最长公共子序列)
答案就是R - L + 1 + m - 2 * LCS

2、如果直接求LCS,复杂度是O(NM) 显然是会超时的。
这里考虑用dp[i][j] 表示对于字符串T的前i位与S串的LCS长度为j时,S串最后一位的最靠前的位置。
这里考虑用序列自动机优化,Nex[i][j]表示S串的第i位的下一个字符为j时的最近的一个位置

3、那么可以得到转移方程为
dp[i][j]=min(dp[i][j],dp[i-1][j]) 意思是对于T串的前i位,LCS长度为j时与前i-1位长度LCS长度为j时取一个最小值。
其实直接写dp[i][j]=dp[i-1][j]即可,因为是第一次到dp[i][j]这个状态,所以只需要看看对于前i-1个字符是不是能形成LCS长度为j

dp[i][j]=min(dp[i][j],Nex[dp[i-1][j-1]][t[i]-‘a’]
这个意思是,当LCS长度为j-1时候,找到在T串的位置,然后找到离这个位置最近的下一个字符为t[i]的位置

其实这两个方程的意思就是说,选不选当前位置作为LCS的最后一个字符。
第一个方程就是说不选t[i]时候,LCS为j时候,在S串中的最靠前的下标。
第二个方程就是说选t[i]作为LCS结尾的字符的时候,在S串中的最靠前的下标

4、注意边界条件。
对于dp[i][0] 需要赋值为l-1,因为对于S串的区间是在[L,R] 那么上一个位置就是L-1。
对于其他的赋值为len(S)+1。
只需要找到最大的j,满足dp[i][j]<=R即可,j就是lcs

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int Nex[N][26],dp[25][25];//dp[i][j]表示t串前i个字符和s串的lcs为j时候 在s串的最小下标
int main(){
    int T;cin>>T;
    while(T--){
        string s,t;cin>>s>>t;
        s=" "+s;
        t=" "+t;
        int lenA=s.size()-1,lenB=t.size()-1;
        for(int i=0;i<26;i++) Nex[lenA][i]=0;
        for(int i=lenA;i;i--){
            for(int j=0;j<26;j++) Nex[i-1][j]=Nex[i][j];
            Nex[i-1][s[i]-'a']=i;
        }
        int Q;cin>>Q;
        while(Q--){
            int l,r;cin>>l>>r;
            int lcs=0;
            for(int i=0;i<=lenB;i++){
                dp[i][0]=l-1;
                for(int j=1;j<=lenB;j++) dp[i][j]=lenA+1;
            }

            for(int i=1;i<=lenB;i++){
                for(int j=1;j<=i;j++){
                    dp[i][j]=dp[i-1][j];///不选t[i]作为lcs的最后一个字符
                    if(Nex[dp[i-1][j-1]][t[i]-'a']) {
                    	///选t[i]作为lcs的最后一个字符
                        dp[i][j]=min(dp[i][j],Nex[dp[i-1][j-1]][t[i]-'a']);
                    }
                    if(dp[i][j]<=r) lcs=max(lcs,j);
                }
            }
            cout<<r-l+1+lenB-2*lcs<<endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不会c语言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值