Description
给定小写字母串 \(s,t\),你可以对 \(s\) 进行以下 \(4\) 种操作:
1. 在任意位置添加一个字符,代价为 \(a\)
2. 删除任一字符,代价为 \(b\)
3. 替换任一字符,代价为 \(c\)
4. 交换相邻两个字符,代价为 \(d\)
求将 \(s\) 变为 \(t\) 的最小代价。
\(|s|,|t|\le 4000,\space 0\lt a,b,c,d\le 10000,\space a+b\le 2d\)
Solution
有一个不存在操作 4 的子任务……
只考虑前 3 个操作的话,由于要成为 \(t\) 串一部分的所有字符之间的相对顺序不会变,这就变成了一个普及组 dp 题。
设 \(f(i,j)\) 表示将 \(s\) 的前 \(i\) 个字符修改为 \(t\) 的前 \(j\) 个字符的代价。
操作1:\(f(i,j)=f(i,j-1)+a\)
操作2:\(f(i,j)=f(i-1,j)+b\)
操作3:\(f(i,j)=f(i-1,j-1)+c\)
然后考虑丧心病狂的操作 \(4\)。
由于 \(a+b\le 2\times d\),因此每个数只会交换一次。(我之前不知道 constraint 里这条是干嘛的)
操作4:记 \(k\) 为 \(s\) 中上一个 \(t[j]\) 的位置,\(l\) 为 \(t\) 中上一个 \(s[i]\) 的位置,则 \(f(i,j)=f(k-1,l-1)+d+(i-k-1)*b+(j-l-1)*a\)
#include<bits/stdc++.h>
#define N 4001
using namespace std;
inline int read(){
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
if(f) return x;
return 0-x;
}
int a,b,c,d,f[N][N],s_lst[27],t_lst[27];
char s[N],t[N]; int n,m;
int main(){
a=read(), b=read(), c=read(), d=read();
scanf("%s %s",s+1,t+1); n=strlen(s+1), m=strlen(t+1);
for(int i=1; i<=m; ++i) f[0][i]=f[0][i-1]+a;
for(int i=1; i<=n; ++i){
f[i][0]=f[i-1][0]+b;
memset(t_lst,0,sizeof t_lst);
for(int j=1; j<=m; ++j){
f[i][j]=min(f[i][j-1]+a,min(f[i-1][j]+b,f[i-1][j-1]+(s[i]==t[j]?0:c)));
int k=s_lst[t[j]-'a'], l=t_lst[s[i]-'a'];
if(k>0 && l>0) f[i][j]=min(f[i][j],f[k-1][l-1]+(i-k-1)*b+d+(j-l-1)*a);
t_lst[t[j]-'a']=j;
}
s_lst[s[i]-'a']=i;
}
printf("%d\n",f[n][m]);
return 0;
}