求两个字符串的最长公共子串、最长公共子序列

题目:输入两个字符串,找出两个字符串中最长的公共子串。

解题思路:

找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。因此我们采用一个二维矩阵来存储中间结果,下面我们看这个二维数组如何构造?

假设两个字符串分别是:”bab”和”caba”。

如果str[i] == str[j] 则matrix[i][j] = 1,否则matrix[i][j] = 0

然后我们从矩阵中找出斜对角线最长的那个子字符串,就是最长公共子串。

即”ab”和”ba”分别为2。

我们可以简化一下,在当我们计算matrix[i][j]时,我们判断str[i] == str[j] 和matrix[i-1][j-1]。

如果str[i] == str[j],则matrix[i][j] = matrix[i-1][j-1] + 1;否则matrix[i][j] = 0。

如下图所示:

image

所以此时,我们只是将matrix[M][N]中,找到最大的值,即为最长公共子串。

然后我们还可以简化一下空间复杂度。

因为我们每判断一个matrix[i][j]时,实际上它只与matrix[i-1][j-1]相关。故所以我们可以使用一维数组来保存上一次的结果。

实现代码:

#include <iostream>
#include <cstring>
using namespace std;

int GetLongestCommonSubString(const char *pStr1, const char *pStr2)
{
    if(pStr1 == NULL || pStr2 == NULL)
        return 0;
    int len1 = strlen(pStr1);
    int len2 = strlen(pStr2);
    int *LCS = new int[len2];
    memset(LCS, 0, sizeof(int)*len2);

    int maxLongSubstring = 0;
    
    for(int i = 0; i < len1; i++)
    {
        for(int j = len2-1; j >= 0; j--)
        {
            if(pStr2[j] == pStr1[i])
            {
                if(j == 0)
                    LCS[j] = 1;
                else
                    LCS[j] = LCS[j-1] + 1;
                
            }
            else
                LCS[j] = 0;
            if(LCS[j] > maxLongSubstring)
                maxLongSubstring = LCS[j];
        }
    }
    delete [] LCS;
    return maxLongSubstring;
    
}
int main(void)
{
    int ret = GetLongestCommonSubString("bab","caba");
    cout<<ret<<endl;
    return 0;
}

结果:

image

 

题目2:输入两个字符串,求两个字符串的最长公共子序列。

首先,最长公共子序列与最长公共子串不同,子序列不要求其在原字符串是连续的。例如字符串X={A,B,C,B,D,A,B},Y = {B,D,C,A,B,A},则X与Y的最长公共子序列为Z={B,C,B,A}。

我们假设X={x1, x2, x3, …, xm},则X的前缀,Xi = {x1, x2, … ,xi}。即X={A,B,C,B,D,A,B},X4={A,B,C,B}。

Y = {y1, y2, y3, … ,yn},则Z={z1, z2, …,zk} 是X和Y的最长公共子序列。

如果xm == yn, 则zk = xm =yn 并且 Zk-1 是Xm-1 和 Yn-1的最长公共子序列。

如果 xm != yn, 则zk != xm,并且Z是Xm-1和Yn的最长公共子序列。

如果 xm != yn, 则zk != yn,并且Z是xm 和Yn-1的最长公共子序列。

所以我们定义了C[i][j]二维数组,用来存储Xi和Yj的最长公共子序列。

                            0                                   如果i==0或者j==0

即C[i][j] =              c[i-1][j-1] + 1                 如果i,j > 0并且 xi == yj

                            Max(c[i][j-1],c[i-1][j])       如果i,j > 0 并且xi != yj

非递归法实现代码:

#include <iostream>
#include <cstring>
using namespace std;

int max(int a, int b)
{
    return a > b ? a : b;
}

int GetLongestCommonSequence(const char *pStr1, const char *pStr2)
{
    /* 判断参数的合法性 */
    if (pStr1 == NULL || pStr2 == NULL)
    {
        return -1;
    }

    int m = strlen(pStr1);
    int n = strlen(pStr2);

    /* 申请二维空间LCS[m+1][n+1] */
    int **LCS = new int*[m+1];
    for (int i = 0; i < m + 1; i++)
    {
        LCS[i] = new int[n+1];
    }

    /* 分别对LCS[i][0], LCS[0][j]赋值为0 */
    for (int i = 0; i < m+1; i++)
    {
        LCS[i][0] = 0;
    }
    for (int j = 0; j < n+1; j++)
    {
        LCS[0][j] = 0;
    }

    /* 分别遍历两个字符串,并更新LCS[i][j] */
    for (int i = 1; i < m+1; i++)
    {
        for (int j = 1; j < n+1; j++)
        {
            if (pStr1[i-1] == pStr2[j-1])
            {
                LCS[i][j] = LCS[i-1][j-1] + 1;
            }
            else
            {
                LCS[i][j] = max(LCS[i-1][j], LCS[i][j-1]);
            }
        }
    }

    /* 获取最长公共子序列 */
    int longestCommonSequence = LCS[m][n];

    /* 删除动态空间 */
    for (int i = 0; i < m + 1; i++)
    {
        delete [] LCS[i];
        LCS[i] = NULL;
    }    
    delete []LCS;
    LCS = NULL;

    /* 返回最长公共子序列 */
    return longestCommonSequence;
}


int main()
{
    int ret = GetLongestCommonSequence("ABCBDAB", "BDCABA");
    cout<<ret<<endl;

}

递归法:

1)设有字符串a[0...n],b[0...m],下面就是递推公式。

             当数组a和b对应位置字符相同时,则直接求解下一个位置;当不同时取两种情况中的较大数值。

实现代码:

#include<stdio.h>
#include<string.h>
char a[30],b[30];
int lena,lenb;
int LCS(int,int);  ///两个参数分别表示数组a的下标和数组b的下标

int main()
{
    strcpy(a,"ABCBDAB");
    strcpy(b,"BDCABA");
    lena=strlen(a);
    lenb=strlen(b);
    printf("%d\n",LCS(0,0));
    return 0;
}

int LCS(int i,int j)
{
    if(i>=lena || j>=lenb)
        return 0;
    if(a[i]==b[j])
        return 1+LCS(i+1,j+1);
    else
        return LCS(i+1,j)>LCS(i,j+1)? LCS(i+1,j):LCS(i,j+1);
}
posted @ 2014-03-21 21:43  mickole  阅读(1425)  评论(0)    收藏  举报