计算两个字符串的距离

     编程之美上看到的一个问题:

许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程度。我们定义了一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
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[1, …, lenA]和B[2, …, lenB]变成相同字符串。

3. 一步操作之后,再将A[2, …, 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])
    {
        return CalculateStringDistance(strA, pABegin + 1, pAEnd, strB,
                                       pBBegin + 1, pBEnd);
    }
    else
    {
        int t1 = CalculateStringDistance(strA, pABegin + 1, pAEnd, strB,
                                         pBBegin + 2, pBEnd);
        int t2 = CalculateStringDistance(strA, pABegin + 2, pAEnd, strB,
                                         pBBegin + 1, pBEnd);
        int t3 = CalculateStringDistance(strA, pABegin + 2, pAEnd, strB,
                                         pBBegin + 2, pBEnd);
        return minValue(t1,t2,t3) + 1;
    }
}
上面的递归程序,有什么地方需要改进呢?在递归的过程中,有些数据被重复计算了。比如,如果开始我们调用CalculateStringDistance(strA, 1, 2, strB, 1, 2),

图3-4 是部分展开的递归调用:


可以看到,圈中的两个子问题被重复计算了。为了避免这种不必要的重复计算,可以把子问题计算后的解存储起来。如何修改递归程序呢?这个问题就留给读者自己完成吧!


作者的思路作为借鉴,很快的写出我们的代码:

#include <string>
#include <cstdio>
#include <iostream>

using namespace std ;

int minValue(int a, int b,int c);
int CalculateStringDistance(string strA, int pABegin, int pAEnd, string strB,int pBBegin, int pBEnd);

int main()
{
    string str1="hello",str2="jjjjj";
    cout << CalculateStringDistance(str1,0,4,str2,0,4) << endl;
    return 0;
}
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])
    {
        return CalculateStringDistance(strA, pABegin + 1, pAEnd, strB,pBBegin + 1, pBEnd);
    }
    else
    {
        int t1 = CalculateStringDistance(strA, pABegin + 0, pAEnd, strB,pBBegin + 1, pBEnd);//add
        int t2 = CalculateStringDistance(strA, pABegin + 1, pAEnd, strB,pBBegin + 0, pBEnd);//remove
        int t3 = CalculateStringDistance(strA, pABegin + 1, pAEnd, strB,pBBegin + 1, pBEnd);//modify
        return minValue(t1,t2,t3) + 1;
    }
}
int minValue(int a,int b,int c)
{
    int tmp = a > b ? b : a;
    if (tmp > c)
    {
        tmp = c;
    }
    return tmp;
}
import java.util.Scanner;

public class ComputeStringDistance {

	public static void main(String[] args) {
		String str1, str2;
		int PABegin = 0, PBBegin = 0;
		Scanner scanner = new Scanner(System.in);
		System.out.print("Input first string:");
		str1 = scanner.next();
		System.out.print("Input second string:");
		str2 = scanner.next();
		int PAEnd = str1.length() - 1;
		int PBEnd = str2.length() - 1;
		System.out.println("The string distance is:"+computeStringDistance(str1, PABegin, PAEnd, str2,
				PBBegin, PBEnd));
		scanner.close();
	}

	public static int computeStringDistance(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.charAt(PABegin) == strB.charAt(PBBegin)) {
			return computeStringDistance(strA, PABegin + 1, PAEnd, strB,
					PBBegin + 1, PBEnd);
		} else {
			int t1 = computeStringDistance(strA, PABegin + 0, PAEnd, strB,
					PBBegin + 1, PBEnd);// add
			int t2 = computeStringDistance(strA, PABegin + 1, PAEnd, strB,
					PBBegin + 0, PBEnd);// remove
			int t3 = computeStringDistance(strA, PABegin + 1, PAEnd, strB,
					PBBegin + 1, PBEnd);// modify
			return minValue(t1, t2, t3) + 1;
		}
	}

	public static int minValue(int t1, int t2, int t3) {
		int tmp = t1 > t2 ? t2 : t1;
		if (tmp > t3) {
			tmp = t3;
		}
		return tmp;
	}

}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值