题目
给定两个字符串 AA 和 BB,现在要将 AA 经过若干操作变为 BB,可进行的操作有:
- 删除–将字符串 AA 中的某个字符删除。
- 插入–在字符串 AA 的某个位置插入某个字符。
- 替换–将字符串 AA 中的某个字符替换为另一个字符。
现在请你求出,将 AA 变为 BB 至少需要进行多少次操作。
输入格式
第一行包含整数 nn,表示字符串 AA 的长度。
第二行包含一个长度为 nn 的字符串 AA。
第三行包含整数 mm,表示字符串 BB 的长度。
第四行包含一个长度为 mm 的字符串 BB。
字符串中均只包含大小写字母。
输出格式
输出一个整数,表示最少操作次数。
数据范围
1≤n,m≤10001≤n,m≤1000
输入样例:
10 AGTCTGACGC 11 AGTAAGTAGGC
输出样例:
4
思路 dp
这题一开始想的时候是BFS枚举所有状态,但是插入就有点麻烦了,因为位置不确定,所以这样暴力就很难想了。 没想到这种题也可以有dp,确实dp能够应用到任何可以进行状态转移的地方。和最长公共子序列很相似。
设\(dp[i][j]\)表示字符\(a\)的前\(i\)个字符和字符\(b\)的前\(j\)个字符匹配所需的最小操作数,不考虑前\(i,j\)个字符,可以肯定这种状态是无后效性的,即前面做出的改变并不会影响后面的状态递推。
我们假设\(a[i] != b[j]\), 若\(a\)和\(b\)若进行删除操作则之前的状态为\(f[i - 1][j]\),若执行插入操作则之前的状态为\(f[i - 1][j]\), 若执行替换操作则之前的状态为\(f[i - 1][j - 1]\)
\(dp[i][j]=max(dp[i - 1][j] + 1, dp[i - 1][j] + 1, dp[i - 1][j - 1] + (int)(a[i] == a[j]))\)
Code
#include <iostream>
using namespace std;
char a[1010], b[1010];
int dp[1010][1010];
// dp[i][j]表示对于考虑串a的前i个字符和串b的前j个字符的最小操作数
int main() {
int n, m;
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
cin >> m;
for(int i = 1; i <= m; i ++) cin >> b[i];
for(int i = 1; i <= n; i ++) dp[i][0] = i;
for(int i = 1; i <= m; i ++) dp[0][i] = i;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + (int)(a[i] != b[j]));
}
}
cout << dp[n][m] << "\n";
}