《编程之美》——计算字符串的相似度

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

分析与解法:
不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。我们还是就住集中考虑如何才能把这个问题转化成规模较小的同样的子问题。如果有两个串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]变成相同字符串。

原始解法:递归法

int CalculateStringDistance(string strA, int pABegin, int pAEnd, string strB, int pBBegin, int pBEnd)
{
    if(pABegin > pAEnd)
    {
        if(pBBegin > pBEnd)
            return 0;
        else
            return pBEnd - pBBegin + 1;
    }
    if(pBBegin > pBEnd)
    {
        if(pABegin > pAEnd)
            return 0;
        else
            return pAEnd - pABegin + 1;
    }
    if(strA[pABegin] == strB[pBBegin])
    {
        CalculateStringDistance(strA, pABegin + 1, pAEnd, strB, pBBegin + 1, pBEnd);
    }
    else
    {
        int t1 = CalculateStringDistance(strA, pABegin + 1, pAEnd, strB, pBBegin, pBEnd);//A串删除字符
        int t2 = CalculateStringDistance(trA, pABegin, pAEnd, strB, pBBegin + 1, pBEnd);//A串插入字符
        int t3 = CalculateStringDistance(strA, pABegin + 1, pAEnd, strB, pBBegin + 1, pBEnd);//A串替换字符
        return minValue(t1, t2, t3) + 1;
    }
}

通过以上1和6,2和5,3和4的结合操作,最后两个字符串每个对应的字符会相同,但是这三种操作产生的最终的两个字符串是不一样的。因为不知道通过上述的三种结合哪种使用的操作次数是最少的,所以要比较操作次数来求得最小值。

之所以在return minValue(a, b, c) + 1,进行加1的操作,是为了表示在当前两个对应位置的字符不相等的时候,采取了一次操作,不管是上述6种情况中的哪一种。而当两个字符相等的时候,就不需要加1,因为没有进行操作。

其时间复杂度最好时是O(n),即全部替换;最差时是O(3^n),几乎没有实际应用价值。

优化

《编程之美》——计算字符串的相似度_第1张图片

可以看出,圈子中的两个子问题被重复计算了, 为了避免这种不必要的计算,可以把子问题计算后的解存储起来

其他解法:动态规划法
动态规划法
图表分析

你可能感兴趣的:(《编程之美》——计算字符串的相似度)