一、题目
https://www.acwing.com/problem/content/904/
二、思想
f[i][j]
为a
的前i
个子母等于b
的前j
个字母所需要的操作次数的集合,题目要求的是最小值,针对a的前i个字母
变成b的前j个字母
有三种操作方式
- 增:可以由
a的前i个字母
末尾增加一个b[j]
来实现,可以用f[i][j-1]+1
表示,加的那个1是本次增加操作的操作次数
因为
f[i][j-1]
所表示的是a
的前i
个子母等于b
的前j-1
个字母所需要的操作次数的集合的最小值,f[i][j-1]
是使他们相等的操作次数,所以a
的前i
个子母跟b
的前j-1
个字母一定是相等的,只需要考虑a[i]
和b[j]
即可
动规即只考虑这个当前情况,由哪个状态转换过来默认这个状态已经是满足题意的了
- 删:可以由
a的前i个字母
末尾减少一个a[i]
来实现,可以用f[i-1][j]+1
表示 - 改:改分两种情况
- 若
a[i]
和b[j]
相等,那就不用改,只需要保证a
的前i-1
个字母与b
的前j-1
个字母相等即可,所以可以用f[i-1][j-1]
表示 - 若
a[i]
和b[j]
不相等,那就需要把a[i]
改成b[j]
,并且需要保证a
的前i-1
个字母与b
的前j-1
个字母相等即可,所以可以用f[i-1][j-1]+1
表示
这三种情况都能使a
的前i
个子母等于b
的前j
个字母,所以三个操作取最小值即可
三、代码 O(n2)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
String a_s = in.next();
int m = in.nextInt();
String b_s = in.next();
int[][] f = new int[1005][1005];
char[] a = new char[1005];
char[] b = new char[1005];
for (int i = 1; i <= n; i++) a[i] = a_s.charAt(i-1);
for (int i = 1; i <= m; i++) b[i] = b_s.charAt(i-1);
// 初始化
// a的前0个字母要与b的前i个字母相同需要进行i次“增加”操作
for (int i = 0; i <= m; i++) f[0][i] = i;
// a的前i个字母要与b的前0个字母相同需要进行i次“删除”操作
for (int i = 0; i <= n; i++) f[i][0] = i;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j] = Math.min(f[i][j-1]+1,f[i-1][j]+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]);
}
}