面试题--编辑距离(动态规划 实用算法)

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.一般情况

我们考虑到字符的操作有三种: 删除, 增加, 替换.
那么它们的操作就是在前一步的操作下以最少的次数增删改.现在分三种情况:

  1. 设可以在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)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值