編程之美 - 計算字符串相似度

問題描述:

有兩個不同的字符串,通過使用一套操作方法可以把兩個字符串變成一樣的。


例如:

1)  "a" 和 "b"  ==>  把a變成b,或把b變成a  變化了一次
2)  "abc" 和 "ade"  ==>  把bc變成de,或把de變成bc  變化了兩次
3)  "abcd" 和 "abcde"  ==>  刪除e  變化了一次

每操作一次,兩個字符串的距離就加 1。

1)  "a" 和 "b" 的距離爲 1
2)  "abc" 和 "ade"  的距離爲 2
3)  "abcd" 和 "abcde" 的距離爲 1

相似度爲距離的倒數。


思考方法:


1  使用遞歸的思考方式:



字符串A和B,假設A中的一個字符和B中的一個字符比較後不一樣。

可以使用下面幾種操作將它們變爲一樣的:
    -    修改 A的字符
    -    修改 B的字符
    -    刪除 A的字符
    -    刪除 B的字符
    -    增加 A中的一個字符
    -    增加 B中的一個字符


因爲只需要計算字符串間的距離,所以並不需要記錄具體的操作形式,但無論怎樣操作,A和B的距離增加了 1。

t1   ==>  遞歸時,考慮A串不動,操作B串
t2   ==>  遞歸時,考慮B串不動,操作A串
t3   ==>  遞歸時,考慮同時操作A串和B串


int calc_distance(char* strA, int nABegin, int nAEnd, char* strB, int nBBegin, int nBEnd)
{
    if (strA[nABegin] == strB[nBBegin])
    {
        return calc_distance(strA, nABegin+1, nAEnd, strB, nBBegin+1, nBEnd);
    }
    else
    {
        t1 = calc_distance(strA, nABegin, nAEnd, strB, nBBegin+1, nBEnd);
        t2 = calc_distance(strA, nABegin+1, nAEnd, strB, nBBegin, nBEnd);
        t3 = calc_distance(strA, nABegin+1, nAEnd, strB, nBBegin+1, nBEnd);


        min = minval(t1,t2,t3)+1;
        return min;
    }
}



2  使用動態規劃的思考方式:

用  記錄分別對字符串A和B同時操作

record[x][y-1]       ==>  用來記錄,考慮A串不動,操作B串後的操作次數
record[x-1][y]       ==>  用來記錄,考慮B串不動,操作A串後的操作次數
record[x-1][y-1]   ==>  用來記錄,考慮同時操作A串和B串的操作次數


然後取其中最小的值 加 1 賦值給  record[x][y]。這樣經過操作後record[lenA][lenB]就是最後的結果。


int calc_distance2(char* strA, int lenA, char* strB, int lenB)
{
    int** record = NULL;
    int i = 0, j = 0;
    int ret = 0;
    record = new int*[lenA+1];
    for (i = 0; i < lenA+1; i++)
    {
        record[i] = new int[lenB+1];
        memset(record[i],0,sizeof(int)*(lenB+1));
    }

    for (i = 1; i <= lenA; i++)
    {
        record[i][0] = i;
    }
    for (j = 1; j <= lenB; j++)
    {
        record[0][j] = j;
    }
    record[0][0] = 0;
    for (i = 1; i <= lenA; i++)
        for (j = 1; j <= lenB; j++)
        {
            if (strA[i-1] == strB[j-1])
            {
                record[i][j] = record[i-1][j-1];
            }
            else
            {
                record[i][j] = minval(record[i-1][j-1], record[i][j-1], record[i-1][j]) + 1;
            }
        }

    ret = record[lenA][lenB];
    for (i = 0; i < lenA+1; i++)
    {
        delete[] record[i];
    }
    delete[] record;

    return ret;
}



代碼:


#include <iostream>
using namespace std;

int minval(int a, int b, int c)
{
    int min = 0;
    if (a < b)
    {
        if (c < a)
            min = c;
        else
            min = a;
    }
    else
    {
        if (c < b)
            min = c;
        else
            min = b;
    }
    return min;
}

int calc_distance1(char* strA, int nABegin, int nAEnd, char* strB, int nBBegin, int nBEnd)
{
    int t1 = 0, t2 = 0, t3 = 0;
    int min = 0;
    if (nABegin > nAEnd)
    {
        (nBBegin > nBEnd) ? min = 0: min = nBEnd - nBBegin + 1;
        return min;
    }

    if (nBBegin > nBEnd)
    {
        (nABegin > nAEnd) ? min = 0: min = nAEnd - nABegin + 1;
        return min;
    }

    cout << "compare: " << strA[nABegin] << ":" << strB[nBBegin] << endl;
    if (strA[nABegin] == strB[nBBegin])
    {
        return calc_distance1(strA, nABegin+1, nAEnd, strB, nBBegin+1, nBEnd);
    }
    else
    {
        t1 = calc_distance1(strA, nABegin, nAEnd, strB, nBBegin+1, nBEnd);
        t2 = calc_distance1(strA, nABegin+1, nAEnd, strB, nBBegin, nBEnd);
        t3 = calc_distance1(strA, nABegin+1, nAEnd, strB, nBBegin+1, nBEnd);

        min = minval(t1,t2,t3)+1;
        return min;
    }
}

int calc_distance2(char* strA, int lenA, char* strB, int lenB)
{
    int** record = NULL;
    int i = 0, j = 0;
    int ret = 0;

    record = new int*[lenA+1];
    for (i = 0; i < lenA+1; i++)
    {
        record[i] = new int[lenB+1];
        memset(record[i],0,sizeof(int)*(lenB+1));
    }

    for (i = 1; i <= lenA; i++)
    {
        record[i][0] = i;
    }
    for (j = 1; j <= lenB; j++)
    {
        record[0][j] = j;
    }

    record[0][0] = 0;
    for (i = 1; i <= lenA; i++)
        for (j = 1; j <= lenB; j++)
        {
            if (strA[i-1] == strB[j-1])
            {
                record[i][j] = record[i-1][j-1];
            }
            else
            {
                record[i][j] = minval(record[i-1][j-1], record[i][j-1], record[i-1][j]) + 1;
            }
        }

    ret = record[lenA][lenB];
    for (i = 0; i < lenA+1; i++)
    {
        delete[] record[i];
    }
    delete[] record;
    return ret;
}

void main()
{
    int dist = 0;
    //char* testA = "abc";
    //char* testB = "abcd";

    //char* testA = "abce";
    //char* testB = "abcd";

    //char* testA = "abcwyz";
    //char* testB = "abcmno";

    char* testA = "a1b2c3d4";
    char* testB = "abcd";

    //char* testA = "xxa1b2c3d4";
    //char* testB = "abcd";

    int lenA = strlen(testA);
    int lenB = strlen(testB);

    //dist = calc_distance1(testA, 0, lenA-1, testB, 0, lenB-1);
    dist = calc_distance2(testA, lenA, testB, lenB);

    cout << "A=" << testA << "  B=" << testB << "   distance=" << dist << endl;
    cin >> dist;
}






















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章