Description
Solution
乍一看,感觉还是挺难的,其实没有那么难,就是一个很暴力的 (dp)(我也不知道为什么有状压的标签)。
先定义一下 (dp) 状态:设 (dp[i][j]) 表示目标串完成到第 (i) 个字符,源串删除到第 (j) 个字符。
初始值:
(dp[0][j] = cost_{delete} * j)
(dp[i][0] = cost_{insert} * i)
嗯,很显然。
下面我们来分析一下每个操作。
insert
插入操作:(dp[i][j] = min(dp[i][j], dp[i - 1][j] + cost_{insert}))
不需要进行判断。
delete
删除操作;(dp[i][j] = min(dp[i][j], dp[i][j - 1] + cost_{delete})
很容易想到吧,且这个操作同样无需判断。
replace
替换操作:(dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + cost_{replace}))
顾名思义,都要进行替换了,还判断什么呢?
copy
复制操作:(dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + cost_{copy}))
这个就需要进行判断了,当源串 (x_j) 等于目标串 (y_i) 时,才能进行转移。
twiddle
交换并复制,这个就有点麻烦了。
先判断,当 (i) 和 (j) 都大于 1,且 (x_j == y_{i - 1}) && (y_i == x_{j - 1})时,才能进行转移。
转移方程:(dp[i][j] = min(dp[i][j], dp[i - 2][j - 2] + cost_{twiddle}))
kill
删除一段字符。
这个怎么转移呢?
很简单,单独拎出来就好了。
最后再枚举一遍源串,然后对 (dp[leny][i] + cost_{kill}) 取个 (min) 即可。
输出的时候再和 (dp[leny][lenx]) 取较小值输出。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
char a[1010], b[1010];
int del, rep, cop, ins, twi;
int dp[210][210];
int main(){
scanf("%s%s", a + 1, b + 1);
int la = strlen(a + 1);
int lb = strlen(b + 1);
scanf("%d%d%d%d%d", &del, &rep, &cop, &ins, &twi);
memset(dp, 127, sizeof(dp));
dp[0][0] = 0;
for(int i = 1; i <= la; i++)
dp[0][i] = del * i;
for(int i = 1; i <= lb; i++)
dp[i][0] = ins * i;
for(int i = 1; i <= lb; i++)
for(int j = 1; j <= la; j++){
dp[i][j] = min(dp[i][j], dp[i][j - 1] + del);
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + rep);
if(a[j] == b[i])
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + cop);
dp[i][j] = min(dp[i][j], dp[i - 1][j] + ins);
if(i > 1 && j > 1 && a[j] == b[i - 1] && b[i] == a[j - 1])
dp[i][j] = min(dp[i][j], dp[i - 2][j - 2] + twi);
}
int ans = 0x7fffffff;
for (int i = 1; i < la; i++)
ans = min(ans, dp[lb][i] + (la - i) * del - 1);
ans = min(ans, dp[lb][la]);
printf("%d
", ans);
return 0;
}