Description
给定小写字母串 (s,t),你可以对 (s) 进行以下 (4) 种操作:
1. 在任意位置添加一个字符,代价为 (a)
2. 删除任一字符,代价为 (b)
3. 替换任一字符,代价为 (c)
4. 交换相邻两个字符,代价为 (d)
求将 (s) 变为 (t) 的最小代价。
(|s|,|t|le 4000,space 0lt a,b,c,dle 10000,space a+ble 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+ble 2 imes 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
",f[n][m]);
return 0;
}