跟着编程之美学算法——字符串相似度
2013-05-27 09:25
267 查看
计算字符串的相似度,这个问题是《编程之美》中的一个经典问题,问题的解决其实应该使用动态规划来解决,在《编程之美》中其实已经给出了该问题的子问题,不过最终用了递归的方法来实现,这使得算法有许许多多的重复计算,算法的复杂度达到了指数级。本文对该问题重新分析,用动态规划的方法来实现,是复杂度降为O(m*n)(其中m、n分别为两个字符串的长度)。
对于一个动态规划问题,那么解决这个问题的第一步就是找到子问题。这里利用《编程之美》中的分析方法:
不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。
我们还是就住集中考虑如何才能把这个问题转化成规模较小的同样的子问题。如果有两个串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[2,...,lenA]和B[2,...,lenB]变成相字符串。
3.一步操作之后,再将A[1,...,lenA]和B[2,...,lenB]变成相字符串。
总结:
如果两个字符串A,B,若第一个字母相同,那么,其距离d就为distance(A[2-n],B[2-m])。
若首字母不相同,那么距离d就为min(distance(A[1-n],B[2-m]), distance(A[2-n],B[1-m]), distance(A[2-n],B[2-m]))+1。
这样,很快就可以完成一个递归程序。这个程序也就是《编程之美》给我们的程序,如下:
View Code
另类解法:
求两个字符串的编辑距离,也可以通过两个字符串的最长公共子序列的方法来求解:
首先求两个字符串的最长公共子序列,方法如:http://www.cnblogs.com/liyukuneed/archive/2013/05/22/3090597.html
比如要计算A、B两个字符串的编辑距离,同时已经得到A、B的最长公共子序列为len,A的长度为lenA,B的长度为lenB。
这样,A与B的编辑距离 = max(lenA - len, lenB - len)。
对于一个动态规划问题,那么解决这个问题的第一步就是找到子问题。这里利用《编程之美》中的分析方法:
不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。
我们还是就住集中考虑如何才能把这个问题转化成规模较小的同样的子问题。如果有两个串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[2,...,lenA]和B[2,...,lenB]变成相字符串。
3.一步操作之后,再将A[1,...,lenA]和B[2,...,lenB]变成相字符串。
总结:
如果两个字符串A,B,若第一个字母相同,那么,其距离d就为distance(A[2-n],B[2-m])。
若首字母不相同,那么距离d就为min(distance(A[1-n],B[2-m]), distance(A[2-n],B[1-m]), distance(A[2-n],B[2-m]))+1。
这样,很快就可以完成一个递归程序。这个程序也就是《编程之美》给我们的程序,如下:
#include <iostream> using namespace std; int Min(int a, int b, int c) { int min = a; if(a > b) min = b; if(min > c) min = c; return min; } int GetDistance_DP(char *str1, char *str2) { if(str1 == NULL || str2 == NULL) return 0; int nLen1 = strlen(str1); int nLen2 = strlen(str2); if(nLen1 == 0) return nLen2; if(nLen2 == 0) return nLen1; int c[nLen1+1][nLen2+1]; for(int i = 0; i <= nLen1; i++) c[i][0] = i; for(int i = 0; i <= nLen2; i++) c[0][i] = i; for(int i = 0; i <= nLen1; i++) { for(int j = 0; j <= nLen2; j++) { if(str1[i] == str2[j]) { c[i][j] = c[i-1][j-1]; } else { c[i][j] = Min(c[i-1][j], c[i][j-1], c[i-1][j-1]) + 1; } } } int result = c[nLen1][nLen2]; return result; } int main() { cout<<GetDistance_DP("xabcdae", "xfdfa")<<endl; return 0; }
View Code
另类解法:
求两个字符串的编辑距离,也可以通过两个字符串的最长公共子序列的方法来求解:
首先求两个字符串的最长公共子序列,方法如:http://www.cnblogs.com/liyukuneed/archive/2013/05/22/3090597.html
比如要计算A、B两个字符串的编辑距离,同时已经得到A、B的最长公共子序列为len,A的长度为lenA,B的长度为lenB。
这样,A与B的编辑距离 = max(lenA - len, lenB - len)。
相关文章推荐
- 跟着编程之美学算法——最长递增子序列
- 跟着编程之美学算法——最长公共子序列
- 跟着编程之美学算法——数组分割
- 跟着编程之美学算法——最长递增子序列
- 计算字符串的相似度--编程之美3.3
- 计算字符串相似度算法——Levenshtein
- 编程之美-计算字符串的相似度
- Levenshtein Distance(LD)-计算两字符串相似度算法
- 编辑距离LCS算法详解:Levenshtein Distance算法计算两个字符串的相似度
- 字符串相似度的算法
- 程序员编程艺术(算法卷):第一章、左旋转字符串
- 计算两个字符串相似度的算法
- 字符串相似度算法
- 字符串相似度算法
- 经典算法题每日演练——第五题 字符串相似度
- 编程之美 - 计算字符串相似度
- 字符串相似度算法
- 相似度算法(二)-------最大公共字符串
- 计算字符串相似度算法——Levenshtein
- 编程珠玑--杂技算法--字符串、数组的旋转