两个字符串的最长连续公共子串

Posted 小河沟大河沟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了两个字符串的最长连续公共子串相关的知识,希望对你有一定的参考价值。

    LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。引入:

LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。

比如:

  String str1 = new String("adbccadebbca");
  String str2 = new String("edabccadece");
str1与str2的公共子串就是bccade.

解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

 

 下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232


  0 0 0 1 0 0 0 1 1 0 0 1 0 0 
  0 1 0 0 0 0 0 0 0 1 1 0 0 0 
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 
  0 1 0 0 0 0 0 0 0 1 1 0 0 0 
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 
  0 0 0 0 0 1 0 0 0 0 0 0 0 0 
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。下面是新的矩阵生成方式: 
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 
  0 1 0 0 0 0 0 0 0 2 1 0 0 0 
  1 0 2 0 1 0 1 0 0 0 0 0 1 0 
  0 2 0 0 0 0 0 0 0 1 1 0 0 0 
  1 0 3 0 1 0 1 0 0 0 0 0 1 0 
  0 0 0 4 0 0 0 2 1 0 0 1 0 0 
  1 0 1 0 5 0 1 0 0 0 0 0 2 0 
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 
  0 0 0 2 0 0 0 2 1 0 0 1 0 0 
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 
  0 0 0 0 0 1 0 0 0 0 0 0 0 0 
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。

  这样做速度比较快,但是花的空间太多。我们注意到在改进的矩阵生成方式当中,每生成一行,前面的那一行就已经没有用了。因此我们只需使用一维数组即可。最终的代码如下:(源代码有些许瑕疵,已改进)

 

void getLCS(char* str1, char* str2)
{
    int len1 = strlen(str1);
    int len2 = strlen(str2);
    int *matrix = new int[len1]; //str1为x方向
    //初始化matrix
    for (int i = 0; i < len1; i++)
    {
        matrix[i] = 0;
    }
    int *maxValue = new int[len2];//str2为y方向,最多有len2个最大值
    int *maxIndex = new int[len2];
    //初始化maxValue和maxIndex
    for (int i = 0; i < len2; i++)
    {
        maxValue[i] = -1;
        maxIndex[i] = -1;
    }
    for (int i = 0; i < len2; i++)
    {
        for (int j = len1 - 1; j >= 0; j--)
        {
            //扫描str1与str2的各个字符的匹配情况
            if (str2[i] == str1[j])
            {
                if (j == 0)
                {
                    matrix[j] = 1;
                }
                else
                {
                    matrix[j] = matrix[j - 1] + 1;
                }
            }
            else
            {
                matrix[j] = 0;
            }
            if (matrix[j] != 0 && matrix[j] > maxValue[0])
            {
                //更新maxValue的值
                maxValue[0] = matrix[j];
                maxIndex[0] = j;
                //将其他的maxValue重置
                for (int i = 1; i < len2; i++)
                {
                    maxValue[i] = -1;
                    maxIndex[i] = -1;
                }
            }
            else if (matrix[j] == maxValue[0])//有多个最大连续公共子串
            {
                for (int i = 1; i < len2; i++)
                {
                    if (maxValue[i] == -1)
                    {
                        maxValue[i] = matrix[j];
                        maxIndex[i] = j;
                        break; //只需加一个
                    }
                }
            }
        }
    }
    for (int i = 0; i<len2; i++)
    {
        if (maxValue[i]>0)
        {
            cout << "" << i + 1 << "个公共子串" << endl;
            for (int j = maxIndex[i] - maxValue[i] + 1; j <= maxIndex[i]; j++)
            {
                cout << str1[j];
            }
            cout << endl;
        }
    }
}

 

以上是关于两个字符串的最长连续公共子串的主要内容,如果未能解决你的问题,请参考以下文章

最长公共子串(动态规划)

最长公共子串

寻找最长公共子串(高分)

求字符串的最长公共子串

最长公共连续子串--全国模拟

动态规划-----最长公共连续子串