原题链接
题目描述
给定两个字符串 A A A 和 B B B,现在要将 A A A 经过若干操作变为 B B B,可进行的操作有:
- 删除–将字符串 A A A 中的某个字符删除。
- 插入–在字符串 A A A 的某个位置插入某个字符。
- 替换–将字符串 A A A 中的某个字符替换为另一个字符。
现在请你求出,将 A A A 变为 B B B 至少需要进行多少次操作。
输入格式
第一行包含整数 n n n,表示字符串 A A A 的长度。
第二行包含一个长度为 n n n 的字符串 A A A。
第三行包含整数 m m m,表示字符串 B B B 的长度。
第四行包含一个长度为 m m m 的字符串 B B B。
字符串中均只包含大小写字母。
输出格式
输出一个整数,表示最少操作次数。
数据范围
1 ≤ n , m ≤ 1000 1 \le n, m \le 1000 1≤n,m≤1000
输入样例:
10
AGTCTGACGC
11
AGTAAGTAGGC
输出样例:
4
算法
(线性 d p dp dp) O ( n m ) O(nm) O(nm)
本题比较难想的就是如何去表示我们的动态规划状态,根据以往的经验,很多动态规划的题目基本都是考虑我们最后一步或者倒数第二部的状态。本题可以考虑把 a a a数组的前 i i i个字母与 b b b数组的前 j j j的字母进行匹配作为状态定义。
如何转移呢?我们可以从题目条件出发,题目给出了增删改 3 3 3种操作,我们通过这 3 3 3种方式将 a a a的前 i i i个字母与 b b b的前 j j j个字母匹配:
- 通过在 a a a的第 i i i个元素后面增加一个元素使得当前的 a a a与 b b b匹配,即 f [ i ] [ j ] = f [ i ] [ j − 1 ] + 1 f[i][j] = f[i][j - 1] + 1 f[i][j]=f[i][j−1]+1
- 通过删除 a a a的第 i i i个元素使得 a a a与 b b b匹配。即 f [ i ] [ j ] = f [ i − 1 ] [ j ] + 1 f[i][j] = f[i - 1][j] + 1 f[i][j]=f[i−1][j]+1
- 通过修改 a a a的第 i i i个元素使得 a a a与 b b b匹配。即 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + 1 f[i][j] = f[i - 1][j - 1] + 1 f[i][j]=f[i−1][j−1]+1
- 如果 a [ i ] a[i] a[i]本身等于 b [ j ] b[j] b[j],则对当前这次不操作。即 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i−1][j−1]
初始化: 通过上述的状态转移方程可以看出,我们的f[i][j]可能会涉及到0
这个下标,所以就需要对
f
f
f进行初始化(许多时候考虑是否要初始化的一个重要依据)。考虑下
f
f
f第一维为0
代表的含义:让
a
a
a的前
0
0
0个字母和
b
b
b的前
i
i
i个字母进行匹配,只有一种最优方法,
b
b
b有多长我们就需要对
a
a
a增加多少个元素让两者匹配,即
f
[
0
]
[
i
]
=
i
f[0][i] = i
f[0][i]=i;再考虑
f
f
f第二维为0
代表的含义:让
a
a
a的前
i
i
i个字母与
b
b
b的前
0
0
0个字母匹配,也只有一种最优方法,
a
a
a有多长就删掉多少个元素,即
f
[
i
]
[
0
]
=
i
f[i][0] = i
f[i][0]=i。
时间复杂度
两重循环,时间复杂度 O ( n m ) O(nm) O(nm)。
J a v a Java Java 代码
import java.util.*;
public class Main {
static final int N = 1010;
static int[][] f = new int[N][N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//读入数据
int n = sc.nextInt();
char[] a = (" " + sc.next()).toCharArray(); //让其下标从1开始
int m = sc.nextInt();
char[] b = (" " + sc.next()).toCharArray();
//初始化
for (int i = 0; i <= n; i ++) f[i][0] = i;
for (int i = 0; i <= m; i ++) f[0][i] = 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] + 1);
else f[i][j] = Math.min(f[i][j], f[i - 1][j - 1]);
}
}
//输出答案
System.out.print(f[n][m]);
}
}