《编程之美》- 3.3 - 计算字符串相似度 即 最小编辑距离问题

题目

3.3计算字符串的相似度
许多程序会大量使用字符串,对于不同的字符串,我们希望能够有办法判断其相似程度。定义下列操作方法使得两个不同的字符串变得相同:
  1. 修改一个字符(如把‘a’改为‘b’)
  2. 增加一个字符(如把‘abdd’变为‘aebdd’)
  3. 删除一个字符(如把‘travelling’变为‘traveling’)
比如,对于‘abcdefg’和字符串‘abcdef’两个字符串来说,我们认为可以通过增加/减少一个‘g’的方式来达到目的。上面的两种方案,都仅需要一次操作。把这个操作所需要的次数定义为两个字符串的距离,而相似度等于“距离+1”的倒数。也就是说,“abcdefg”和“abcdef”的距离为1,则相似度为1/2=0.5;
给定任意两个字符串,写出一个算法计算它们的相似度。

分析

该题目描述的就是经典的最短编辑距离问题。
方法一:递归解决,将大问题转化为规模较小的子问题:
  • 一步操作之后,把sA[2...LenA]和sB[1...LenB]变成相同字符串
  • 一步操作之后,把把sA[1...LenA]和sB[2...LenB]变成相同字符串
  • 一步操作之后,把把sA[2...LenA]和sB[2...LenB]变成相同字符串
方法二:动态规划
首先定义这样一个函数——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。

代码

/*
3.3 计算字符串的相似度
*/

#include <iostream>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>

using namespace std;

/*递归解决--有大量的重复计算数据*/
int calculateStrDistance(string sA, int pAbeg, int pAend, string sB, int pBbeg, int pBend)
{
	if (pAbeg > pAend)
	{
		if (pBbeg > pBend)
		{
			return 0;
		}//if
		else {
			return pBend - pBbeg + 1;
		}//else
	}//if
	else if (pBbeg > pBend)
	{
		if (pAbeg > pAend)
		{
			return 0;
		}//if
		else {
			return pAend - pAbeg + 1;
		}//else
	}//elif	
	else {
		if (sA[pAbeg] == sB[pBbeg])
		{
			return calculateStrDistance(sA, pAbeg + 1, pAend, sB, pBbeg + 1, pBend);
		}
		else {
			/*一步操作之后,把sA[2...LenA]和sB[1...LenB]变成相同字符串*/
			int d1 = calculateStrDistance(sA, pAbeg, pAend, sB, pBbeg + 1, pBend);
			/*一步操作之后,把把sA[1...LenA]和sB[2...LenB]变成相同字符串*/
			int d2 = calculateStrDistance(sA, pAbeg + 1, pAend, sB, pBbeg, pBend);
			/*一步操作之后,把把sA[2...LenA]和sB[2...LenB]变成相同字符串*/
			int d3 = calculateStrDistance(sA, pAbeg + 1, pAend, sB, pBbeg + 1, pBend);

			return min(d1, min(d2, d3)) + 1;
		}//else
		
	}//else
}
/*方法一:*/
int calculateStrDistance(string sA, string sB)
{
	return calculateStrDistance(sA, 0, sA.length()-1, sB, 0, sB.length()-1);
}


/*方法二:动态规划实现--最小编辑距离问题*/
int calculateStrDistance2(string sA, string sB)
{
	if (sA.empty())
		return sB.length();
	else if (sB.empty())
		return sA.length();
	
	int lA = sA.length(), lB = sB.length();
	vector<vector<int> > dp(lA+1, vector<int>(lB+1, 0));

	/*求边界编辑距离值*/
	for (int i = 0; i <= lA; ++i)
	{
		dp[i][0] = i;
	}//for

	for (int j = 0; j <= lB; ++j)
	{
		dp[0][j] = j;
	}//for

	/*求中间的编辑距离值*/
	for (int i = 1; i <= lA; ++i)
	{
		for (int j = 1; j <= lB; ++j)
		{
			int flag = 0;
			if (sA[i - 1] != sB[j - 1])
			{
				flag = 1;
			}//if

			dp[i][j] = min(dp[i - 1][j - 1] + flag, min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
		}//for
	}//for
	return dp[lA][lB];
}


int main()
{
	string sA = "abc", sB = "ac";
	cout << calculateStrDistance(sA, sB) << endl;

	cout << calculateStrDistance2(sA, sB) << endl;
	system("pause");
	return 0;
}



你可能感兴趣的:(《编程之美》- 3.3 - 计算字符串相似度 即 最小编辑距离问题)