首先当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");
}
}
}