codechef Sereja and two strings(动态规划)

首先当a=0或者b=0的时候我们可以直接特判。
当a,b不为0的时候我们考虑dp。
可以先考虑O(n^2)的dp,设dp[i][j]表示第一个字符串走到i,与第二个字符串匹配到j的最小花费,于是可以列出dp转移方程
dp[i][j]=min(dp[i][j],dp[i-1][j]+a) //删除一个字符
dp[i][j]=min(dp[i][j],dp[i-1][j-1]+(s[i]==t[j]?0:b)) //替换一个字符
dp[i][j]=min(dp[i][j],dp[i-1][j-1]+2*a) //删除一个字符再假如一个字符,实测这一个转移可以不用考虑,因为dp[i][j-1]可以转移到,为了以防万一写到代码中的
if(s[i]==t[j-1]) dp[i][j]=min(dp[i][j],dp[i-1][j-2]+a) //添加一个字符

然后这个dp的复杂度太高了肿么办?

注意到题0<=目a,b,k<=100,而对于a,b为0的时候被特判掉了,所以可以保证第一个串匹配i的时候,abs(i-j)<=k/a,为啥呢,因为abs(i-j)!=0时说明我们一定要添加或者删除字符,而我们特判了保证a不等于0,所以最大进行k/a次添加或删除,就gg了,花费就大于k了。
于是我们只需枚举必要的状态即可,没必要的状态就扔掉了。

然后重新设计状态,设dp[i][s]表示第一个字符串走到i,第二个字符串匹配到的位置是i-s的最小花费(-k/a<=s<=k/a),然后由于s可能为负数,我们差分一下给他加上100即可。

时间复杂度O(nk)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e5+5,add=100;
char s[N],t[N];
int n,m,a,b,k,dp[N][204];
int main()
{
    
    
    int T;
    scanf("%d",&T);
    while(T--)
    {
    
    
        scanf("%s%s",s+1,t+1);
        n=strlen(s+1);m=strlen(t+1);
        scanf("%d%d%d",&a,&b,&k);
        if(a==0&&b==0) printf("0\n");
        else if(a==0) printf("0\n");
        else if(b==0)
        {
    
    
            if(n>=m) printf("%d\n",(n-m)*a<=k?(n-m)*a:-1);
            else printf("%d\n",(m-n)*a<=k?(m-n)*a:-1);
        }
        else
        {
    
    
            memset(dp[0],inf,sizeof(dp[0]));
            dp[0][0+add]=0;
            for(int i=1;i<=n;i++)
            {
    
    
                memset(dp[i],inf,sizeof(dp[i]));
                for(int j=max(0,i-k/a);j<=min(m,i+k/a);j++)
                {
    
    
                    int k=j-i+add;
                    dp[i][k]=min(dp[i][k],dp[i-1][j-(i-1)+add]+a);
                    if(j-1>=0)
                        dp[i][k]=min(dp[i][k],dp[i-1][j-1-(i-1)+add]+min(s[i]==t[j]?0:b,2*a));
                    if(j-1>=0&&j-2-(i-1)+add>=0&&s[i]==t[j-1])
                        dp[i][k]=min(dp[i][k],dp[i-1][j-2-(i-1)+add]+a);
                }
            }
            if(m-n<-100||m-n>100) printf("-1\n");
            else if(dp[n][m-n+add]<=k) printf("%d\n",dp[n][m-n+add]);
            else printf("-1\n");
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Huah_2018/article/details/102523301