编辑距离

String 字符串

1、编辑距离

编辑距离,又称Levensgtein距离(莱文斯坦距离也叫做Edit Distance),是指两个字符串之间,有一个转成另一个字符串所需要的最小编辑操作的次数,编辑操作次数包括替换一个字符,添加一个字符,删除一个字符,如果它们的距离越大,说明它们越是不同。
在实际当中的应用有:DNA分析、拼字检查、语音辨识、抄袭侦测……

我们有两个字符串,对于每一位都有可能有三种操作,每一位也都有可能对应着另一位的任意一个字符的位置,出于这个想法我们第一反应就是用一个二重循环来依次比较判断一个字符串的每一个位置与另一个字符串的每一个位置,可是对应的每一步操作我们又应该怎样去实现呢?
我们用 s1[i] 表示第一个字符串的第 i 个位置,用 s2[i] 表示第二个子串的第 i 个位置,我们把 s1 转化为 s2(把s2 转化为 s1 也是同理,相互间的替换很好理解,可以相互转化,s1 转化为 s2 添加字符就相当于s2转化为s1删除字符,同理,s1 转化为s2删除字符就相当于s2转化为s1添加字符,所以我们在这以把s1转化为s2为例):
在这里我们需要用到的动态规划数组dp[i][j]:把s1的前i位转化为s2的前j位需要的操作数:

①:在当前位置插入一个字符:在s1中插入字符的位置有很多,并且在一处我们可以不止插入一个字符,这样我们应该怎样想呢?我们需要知道一点的是虽然可以插入若干字符,但是原有字符的相对位置在插入字符后不发生改变,所以我们在这一步的状态转移方程为:dp[ i ][ j ] = dp[ i ][ j-1 ] + 1,因为我们想要把s1的前 i 位转化为s2的前 j 位,并且要在s1的第 i 个位置插入一个字符,插入我们肯定是插入与s2第 j 位的字符相同的字符,所以我们需要的就是把s1的前 i 位转化为s2的前 j-1 位,然后再在s1中插入一个s2的第 j 位字符,这样就实现了把s1转化为s2的插入操作;

②在当前位置删除一个字符:同理,把s1的前 i 位转化为s2的前 j 位并且删除s1的第 i 位,也就是让s1的前 i-1 位与s2的前 j 位相同,然后删除s1的d第 i 位:dp[i][j] = dp[i-1][ j ] +1

把s1的第 i 位替换为s2的第 j 位:我们需要的上一步就是把s1的前 i-1 位转化为s2的前 j-1 位,然后我们就需要判断s1[ i ] 是否等于 s2[ j ],如果s1[ i ] == s2[ j ],则不需要进行替换操作,否则就要进行替换,操作数+1。
dp[ i ][ j ] = dp[ i-1 ][ j-1 ] + (s1[ i ] != s2[ j ])

综上所述,我们的状态转移方程就写好了:
dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i][j] = dp[i-1][j-1] + (s1[i] != s2[j]);

for(int i=1;i<=s1.length();i++){
		for(int j=1;j<=s2.length();j++){
			dp[i][j] = min(dp[i-1][j],dp[i][j-1])+1;
			dp[i][j] = min(dp[i][j],dp[i-1][j-1]+(s1[i+1]!=s2[j+1]));
		}
	}

状态转移方程写好了,我们就要开始动态规划的前期工作了,也就是动态数组的初始化,根据上面我们对动态数组的定义,dp[i][0]:把s1的前 i 位转化为s2的前0位,也就是把s1变为空,所以需要的操作数为 i 次,即dp[i][0] = i;同理,dp[0][j] = j;

for(int i=0;i<s2.length();i++){
		dp[0][i] = i;
	}
	for(int i=0;i<s1.length();i++){
		dp[i][0] = i;
	}

完整代码如下:

#include<iostream>
#include<string>
using namespace std;
int dp[10001][10001];
int main(){
	string s1,s2;
	cin >> s1 >> s2;
	for(int i=0;i<s2.length();i++){
		dp[0][i] = i;
	}
	for(int i=0;i<s1.length();i++){
		dp[i][0] = i;
	}
	for(int i=1;i<=s1.length();i++){
		for(int j=1;j<=s2.length();j++){
			dp[i][j] = min(dp[i-1][j],dp[i][j-1])+1;
			dp[i][j] = min(dp[i][j],dp[i-1][j-1]+(s1[i+1]!=s2[j+1]));
		}
	}
	cout << dp[s1.length()][s2.length()] << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值