字符串编辑距离(相似度)
2016-07-18 22:06
525 查看
编辑距离及编辑距离算法
编辑距离概念描述:编辑距离,又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
例如将kitten一字转成sitting:
sitten (k→s)
sittin (e→i)
sitting (→g)
俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。
问题:找出字符串的编辑距离,即把一个字符串s1最少经过多少步操作变成编程字符串s2,操作有三种,添加一个字符,删除一个字符,修改一个字符
给定一个源串和目标串,能够对源串进行如下操作:
1.在给定位置上插入一个字符
2.替换任意字符
3.删除任意字符
写一个程序,返回最小操作数,使得对源串进行这些操作后等于目标串,源串和目标串的长度都小于2000。
思路
如果有两个串 A = xabcdae 和 B = xfdfa,它们的第一个字符是相同的,只要计算A[2…7] = abcdae 和 B[2…5] = fdfa的距离就可以了。但是如果两个串的第一个字符不相同,那么可以进行如下的操作(lenA和lenB分别是字符串A和B的长度):
(1)删除A串的第一个字符,然后计算A[2…lenA]和B[1…lenB]的距离。
(2)删除B串的第一个字符,然后计算A[1…lenA]和B[2…lenB]的距离。
(3)修改A串的第一个字符为B串的第一个字符,然后计算A[2…lenA]和B[2…lenB]的距离。
(4)修改B串的第一个字符为A串的第一个字符,然后计算A[2…lenA]和B[2…lenB]的距离。
(5)增加B串的第一个字符到A串的第一个字符之前,然后计算A[1…lenA]和B[2…lenB]的距离。
(6)增加A串的第一个字符到B串的第一个字符之前,然后计算A[2…lenA]和B[1…lenB]的距离。
在这个题目中,我们并不在乎两个字符串变得相等之后的字符串是什么样的。所以,我们可以将上面的6个步骤简化为:
(1)一步操作之后,再将A[2…lenA] 和 B[1…lenB]变成相同的字符串。
(2)一步操作之后,再将A[1…lenA] 和 B[2…lenB]变成相同的字符串。
(3)一步操作之后,再将A[2…lenA] 和 B[2…lenB]变成相同的字符串。
这样,很快就可以完成一个递归程序。
在递归的过程中,有些数据被重复计算了。比如,如果我们开始调用StrDistance(A,1,3,B,1,3)
下图是部分展开的递归调用:
可以看到,圈中的两个子问题被重复计算了。为了避免这种不必要的重复计算,可以把子问题计算后的解储存起来。
思路二
编辑距离是动态规划里面的经典题目。 Edit[i][j]为word1[0..i-1]和word2[0..j-1]的最小编辑数。
状态转移方程:
解析:
首先定义这样一个函数——edit(i, j),它表示第一个字符串的长度为i的子串到第二个字符串的长度为j的子串的编辑距离。
显然可以有如下动态规划公式:
if i == 0 且 j == 0,edit(i, j) = 0
if i == 0 且 j > 0,edit(i, j) = j
if i > 0 且j == 0,edit(i, j) = i
if i ≥ 1 且 j ≥ 1 ,edit(i, j) == min{ edit(i-1, j) + 1, edit(i, j-1) + 1, edit(i-1, j-1) + f(i, j) },当第一个字符串的第i个字符不等于第二个字符串的第j个字符时,f(i, j) = 1;否则,f(i, j) = 0。
0 | f | a | i | l | i | n | g | |
0 | ||||||||
s | ||||||||
a | ||||||||
i | ||||||||
l | ||||||||
n |
0 | f | a | i | l | i | n | g | |
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
s | 1 | |||||||
a | 2 | |||||||
i | 3 | |||||||
l | 4 | |||||||
n | 5 |
0 | f | a | i | l | i | n | g | |
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
s | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
a | 2 | 2 | ||||||
i | 3 | |||||||
l | 4 | |||||||
n | 5 |
0 | f | a | i | l | i | n | g | |
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
s | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
a | 2 | 2 | 1 | 2 | 3 | 4 | 5 | 6 |
i | 3 | 3 | 2 | 1 | 2 | 3 | 4 | 5 |
l | 4 | 4 | 3 | 2 | 1 | 2 | 3 | 4 |
n | 5 | 5 | 4 | 3 | 2 | 2 | 2 | 3 |
<span style="font-family:Microsoft YaHei;font-size:14px;">#include <iostream> #include <string> #include <algorithm> using namespace std; int min(int a, int b, int c) { int tmp = std::min(a, b); return std::min(tmp, c); } //动态规划解法,数据未重复计算 int edit_distance(const string& str1, const string& str2) { int m = str1.size(); int n = str2.size(); if (m == 0 || n == 0) return 0; //建立数组 int **c = new int*[m + 1]; for (int i = 0; i < m + 1; i++) c[i] = new int[n + 1]; //初始化数组 for (int i = 0; i < m + 1; i++) c[i][0] = i; for (int i = 0; i < n + 1; i++) c[0][i] = i; //距离计算 for (int i = 1; i < m+1; i++) { for (int j = 1; j < n+1; j++) { int d = (str1[i-1] == str2[j-1] ? 0 : 1); c[i][j] = min(c[i - 1][j] + 1, c[i][j - 1] + 1, c[i - 1][j - 1] + d); } } return c[m] ; } //递归解法,数据重复计算 int calculateDistance(string str1, int begin1, int end1, string str2, int begin2, int end2) { if (begin1 > end1)//边界条件,str1处理完毕 { if (begin2 > end2) return 0; else return end2 - begin2 + 1; } if (begin2 > end2)//边界条件,str2处理完毕 { if (begin1 > end1) return 0; else return end1 - begin1 + 1; } if (str1[begin1] == str2[begin2]) return calculateDistance(str1, begin1 + 1, end1, str2, begin2 + 1, end2); else { int dis1 = calculateDistance(str1, begin1 + 1, end1, str2, begin2, end2); int dis2 = calculateDistance(str1, begin1, end1, str2, begin2 + 1, end2); int dis3 = calculateDistance(str1, begin1 + 1, end1, str2, begin2 + 1, end2); return min(dis1, dis2, dis3) + 1; } } int main(void) { string str1 = "sailn"; string str2 = "failing"; //动态规划解法,数据未重复计算 int r = edit_distance(str1, str2); cout << "the edit_distance is : " << r << endl; //递归解法,数据重复计算 int l = calculateDistance(str1, 0, str1.size() - 1, str2, 0, str2.size() - 1); cout << "the calculateDistance is : " << l << endl; system("pause"); return 0; }</span>
相关文章推荐
- 数据库链接字符串查询网站
- Flex字符串比较 还有Flex字符串操作
- Ruby中创建字符串的一些技巧小结
- ASP下经常用的字符串等函数参考资料
- 将字符串小写转大写并延时输出的批处理代码
- 将字符串转换成System.Drawing.Color类型的方法
- Lua源码中字符串类型的实现
- Lua性能优化技巧(四):关于字符串
- 字符串聚合函数(去除重复值)
- Ruby中的字符串编写示例
- 总结的5个C#字符串操作方法分享
- sqlserver中求字符串中汉字的个数的sql语句
- sql server字符串非空判断实现方法
- C#算法函数:获取一个字符串中的最大长度的数字
- VBS的字符串及日期操作相关函数
- C#实现将千分位字符串转换成数字的方法
- jquery 删除字符串最后一个字符的方法解析
- PowerShell实现在字符串中查找大写字母
- PowerShell中使用Out-String命令把对象转换成字符串输出的例子
- PowerShell中字符串使用单引号和双引号的区别