题目:
给定两个字符串 A 和 B,现在要将 A 经过若干操作变为 B,可进行的操作有:
- 删除–将字符串 A 中的某个字符删除
- 插入–在字符串 A 的某个位置插入某个字符
- 替换–将字符串 A 中的某个字符替换为另一个字符
现在请你求出,将 A 变为 B 至少需要进行多少次操作
第一行包含整数 n,表示字符串 A 的长度
第二行包含一个长度为 n 的字符串 A
第三行包含整数 m,表示字符串 B 的长度
第四行包含一个长度为 m 的字符串 B
(字符串中均只包含大小写字母)
输出一个整数,表示最少操作次数
1 ≤ n, m ≤ 1000
输入:
10
AGTCTGACGC
11
AGTAAGTAGGC
输出:
4
public class 线性dp_最短编辑距离 {
public static int N = 1010, n, m;
public static char[] a = new char[N], b = new char[N];
public static int[][] f = new int[N][N];
public static void main(String[] args) {
//读入,一般情况下dp问题是从1开始比较好
Scanner in = new Scanner(System.in);
n = in.nextInt(); String A = in.next();
m = in.nextInt(); String B = in.next();
for (int i = 1; i<=n; i++) a[i] = A.charAt(i-1);
for (int i = 1; i<=m; i++) b[i] = B.charAt(i-1);
//先将所有的0进行初始化(因为后续计算中涉及到i-1和j-1,所以必须将0代表的东西表示出来)
for (int i = 0; i<=m; i++) f[0][i] = i; //当a中的前0个字符想和b中的前i个字符匹配的话,只能是进行添加i个字符
for (int i = 0; i<=n; i++) f[i][0] = i; //当a中的前i个字符想和b中的前0个字符匹配的话,只能是进行删除i个字符
//状态计算
for (int i = 1; i<=n; i++) {
for (int j = 1; j<=m; j++) {
//计算部分的分析见文中
f[i][j] = Math.min(f[i-1][j], f[i][j-1]) + 1;
if (a[i] == b[j]) f[i][j] = Math.min(f[i][j], f[i-1][j-1]);
else f[i][j] = Math.min(f[i][j], f[i-1][j-1] + 1);
}
}
//最后输出结果
System.out.println(f[n][m]);
}
}
思路:
经典y式dp法
1.状态表示
f[i][j] :使(A的前 i 个字符)变为(B的前 j 个字符)的最少编辑次数(每次删,增,改皆记为编辑一次)
2.状态计算(状态转移)
分为三种情况来看:
1.删除:A的前 i 位删除 i 后,和B的前 j 位相同,说明A的前 i-1 和B的前 j 相同,删除第 i 位算作一次编辑,最后得到 f[i-1][j] + 1
2.增加:A的前 i 位增加一位,和B的前 j 位相同,说明A的前 i 位和B的前 j-1相同,增加算作一次编辑,最后得到 f[i][j-1] + 1
3.替换:
如果当前A[i] 和 B[j] 相同,那么就直接将 f[i-1][j-1] 转移过来即可
如果不相同的话,那么将A[i] 改为 B[j], 也就是 f[i-1][j-1] + 1,更改算作一次编辑
声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流