难度
普 及 + / 提 高 \color{green}普及+/提高 普及+/提高
不是很难。
但是是线性DP的经典题,所以写一下 \sqrt{}
题意
- 扩展串:在原串的基础上,任意位置添加任意多个(或0个)空格之后形成的串。
- 两个字符串 A 、 B A、B A、B的字串距离等于某对相同长度的扩展串 A ′ 、 B ′ A^\prime、B^\prime A′、B′ 的每一位字符的距离和的最小值。
两个字符的距离:
- 若是两个小写字母,则距离为他们的 A S C I I ASCII ASCII码的差的绝对值
- 若一个是空格一个是小写字母,则距离为给定的 k k k
- 若两个都是空格,则距离为0。
给定两个字符串,求出他们的字串距离。
数据范围
∣ A ∣ , ∣ B ∣ ≤ 2000 |A|,|B|\le 2000 ∣A∣,∣B∣≤2000
1 ≤ k ≤ 100 1\le k\le 100 1≤k≤100
思路
- 画一下图,可以大概知道在最优情况下,肯定是一些字符配对,剩下的字符都和空格配对,这样才可以使得最后的答案尽可能小。
- 这样,我们可能会想到一些线性DP的感觉,数据范围也提示了可以是 O ( ∣ A ∣ × ∣ B ∣ ) O(|A|\times|B|) O(∣A∣×∣B∣)的 D P DP DP 做法。
- 容易想到,我们设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 第一个字符串匹配到第 i i i 位,第二个字符串匹配到第 j j j 位所能得到的字串距离。
- 考虑转移。我们有三种简单的选择:
- 配对这两个位置的字符。
- 第一个串的第 i i i 位的字符和空格匹配。
- 第二个串的第 j j j 位的字符和空格匹配
即:
d p [ i ] [ j ] = { d p [ i − 1 ] [ j − 1 ] + d i s ( A i , B j ) d p [ i − 1 ] [ j ] + k d p [ i ] [ j − 1 ] + k \color{cyan}dp[i][j]= \begin{cases} dp[i-1][j-1]+dis(A_i,B_j)\\ dp[i-1][j]+k\\ dp[i][j-1]+k \end{cases} dp[i][j]=⎩⎪⎨⎪⎧dp[i−1][j−1]+dis(Ai,Bj)dp[i−1][j]+kdp[i][j−1]+k
注意的几个点,字符串尽量让下标从1开始。
初始化 d p [ 0 ] [ j ] dp[0][j] dp[0][j] 以及 d p [ i ] [ 0 ] dp[i][0] dp[i][0] ,因为他们不会自己更新。
核心代码
时间复杂度 O ( ∣ A ∣ × ∣ B ∣ ) O(|A|\times|B|) O(∣A∣×∣B∣)
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
const int MAX = 2e3+50;
const int INF = 0x3f3f3f3f;
string aa,bb;
int dp[MAX][MAX];
int main()
{
int k;
cin >> aa >> bb >> k;
fill(dp[0],dp[0] + MAX * MAX,INF);
aa = " " + aa;
bb = " " + bb;
dp[0][0] = 0;
for(int i = 1;i < aa.size();++i)dp[i][0] = k * i;
for(int i = 1;i < bb.size();++i)dp[0][i] = k * i;
for(int i = 1;i < aa.size();++i){
for(int j = 1;j < bb.size();++j){
dp[i][j] = min(dp[i][j],dp[i-1][j-1] + abs(aa[i] - bb[j]));
dp[i][j] = min(dp[i][j],dp[i][j-1] + k);
dp[i][j] = min(dp[i][j],dp[i-1][j] + k);
}
}
cout << dp[aa.size()-1][bb.size()-1];
return 0;
}