1.题目描述
设A和B是2个字符串。要用最少的字符操作将字符串A转换为字符串B。
这里所说的字符操作包括
(1)删除一个字符;
(2)插入一个字符;
(3)将一个字符改为另一个字符。
将字符串A变换为字符串B所用的最少字符操作数称为字符串A到 B的编辑距离,记为d(A,B)。
对于给定的字符串A和字符串B,计算其编辑距离 d(A,B)。
输入样例
fxpimu
xwrs
输出样例
5
算法描述
这是一个动态规划问题.
我们定义一个二维数组d[i][j]来保存这些编辑最小操作, 它表示将串a[0:i-1]转变为b[0:j-1]的最小步骤.
分情况讨论
1.当 i = 0时, a串为空
那么转变为b串就是不断添加字符, d[0][j] = j. (将字符串a[0]转换为b[j]的最小步骤.
2.当j = 0 时, b串为空
那么转变为b串就是不断删除字符, d[j][0] = i. (将字符串a[i[转换为b[0]的最小步骤).
3.一般情况
我们考虑到字符的操作有三种: 删除, 增加, 替换.
那么它们的操作就是在前一步的操作下以最少的次数增删改.现在分三种情况:
- 设可以在k1个操作内将s[0:i-1]转换为t[0:j],用k1+1次操作将s[0:i]转化为t[0:j],只需要先在“s[0:i]转化为t[0:j]”的操作开端做1次移除操作移除s[i]将s[0:i]转化为s[0:i-1],然后再做k1个操作就可以转换为t[0:j]。对应表格,既矩阵所求d[i][j]的左格。
2)设可以在k2个操作内将s[0:i]转换为t[0:j-1],用k2+1次操作将s[0:i]转化为t[0:j],先用k2次操作将s[0:i]转化为t[0:j-1],然后再执行1次插入操作在“s[0:i]变成t[0:j-1]的操作”的末尾插入“增加t[j]”的一次操作,即可将s[0:i]转化为t[0:j]。对应表格,既矩阵所求d[i][j]的上格。
3)设可以在k3个操作内将s[0:i-1]转化为t[0:j-1] ,若s[i]==t[j],S[0:i]变到t[0:j]就只要k3个操作,若s[i]!=t[j],则需1次换操作加在s[0:i-1]转化为t[0:j-1]的操作数基础上就可以将S[0:i]变到t[0:j],共k3+1次。对应所求d[i][j]的左上格。
动态规划三步:
a.描述最优解的结构
b.递归定义最优解的值
c.按自底向上的方式计算最优解的值
d.由计算出的结构构造一个最优解,利用解决子问题的最优值从而得出原问题的最优值。
子问题标识符的含义:从S[0:i]变到t[0:j]的编辑距离。(1<=i<=m,1<=j<=n)
子问题递归公式:
(n为字符串t的长度,m为字符串s的长度);;
n (m=0);
m (n=0);
d[j][i] = min(d[j-1][i-1]+1, min(d[j][i-1] + 1,d[j-1][i] + 1 ) s[i-1] != t[j-1]
d[j][i] = min(d[j-1][i-1], min(d[j][i-1] + 1,d[j-1][i] + 1 )) s.[i-1] ==t.[j-1]
1<=i<=m,1<=j<=n
原问题最优解: 双重循环扫描完S[0:i]变到t[0:j]后, 即s[0:m] 变到 t[0:n], 最优值为d[n][m]的编辑距离.
对于d[i][j]二维数组, 可以开辟数组d[n+1][m+1] , n为字符串t的长度, m为字符串s的长度.
首先进行如下初始化:
填表方法十分巧妙和便捷,得记牢此方法。
如图, 最优值为d[n][m]即为所求两串的编辑距离.
申请二维数组空间代码: int[][] d = new int[n+1][m+1];
初始化代码:
for(int i = 0;i<=n;++i){
d[i][0]=i;
}
for(int j = 1;j<=m;++j){
d[0][j]=j;
}
扫描数组的代码(即解决每个子问题最终求得原问题的过程的代码):
for(int i = 1;i<=m;++i){
for(int j = 1;j<=n;++j){
if(s.charAt(i-1) == t.charAt(j-1)){
d[j][i] = Math.min(d[j-1][i-1], Math.min(d[j][i-1] + 1, d[j-1][i] + 1));
}
else{
d[j][i] = Math.min(d[j-1][i-1]+1, Math.min(d[j][i-1] + 1,d[j-1][i-1] +1 ));
}
}
}
完整java代码:
import java.util.Scanner;
public class PTA73{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
String a = input.nextline(); //申请字符串
String b = input.nextline();
System.out.println(BJ(a,b)); //调用函数
}
public static int BJ(String s,String t){
int m = s.length();
int n = t.length();
if(m==0) return a;
if(n==0) return b;
int [][] d = new int[n+1][m+1];
for(int i = 0;i<=n;++i){
d[i][0]=i;
}
for(int j = 1;j<=m;++j){
d[0][j] = j;
}
for(int i = 1;i<=m;++i){
for(int j = 1;j<=n;++j){
if(s.chatAt(i-1)==t.chatAt(j-1)){
d[j][i] = Math.min(d[j-1][i-1],Math.min(d[j][i-1]+1,d[j-1][i] + 1));
}
else{
d[j][i] = Math.min(d[j-1][i]+1,Math.min(d[j][i-1]+1,d[j-1][i]+1));
}
}
}
return d[n][m];
}
}
算法时间及空间复杂度分析(此次为java编写):
由上述代码得,求子问题时的双重循环加两个初始化单循环的线性时间,再加上比较等操作的常数时间,得时间复杂度为O(MN),而空间复杂度,花销了一个二维数组的辅助空间,得空间复杂度也为O(MN)。