`
caoruntao
  • 浏览: 480803 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

最长公共子串

 
阅读更多

【转】http://zhedahht.blog.163.com/blog/static/254111742007376431815/

 

题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。

例如:输入两个字符串BDCABAABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子串,则输出它们的长度4,并打印任意一个子串。

分析:求最长公共子串(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,因此一些重视算法的公司像MicroStrategy都把它当作面试题。

完整介绍动态规划将需要很长的篇幅,因此我不打算在此全面讨论动态规划相关的概念,只集中对LCS直接相关内容作讨论。如果对动态规划不是很熟悉,请参考相关算法书比如算法讨论。

先介绍LCS问题的性质:记Xm={x0, x1,…xm-1}Yn={y0,y1,…,yn-1}为两个字符串,而Zk={z0,z1,…zk-1}是它们的LCS,则:

1.       如果xm-1=yn-1,那么zk-1=xm-1=yn-1,并且Zk-1Xm-1Yn-1LCS
2.       如果xm-1≠yn-1,那么当zk-1≠xm-1ZXm-1YLCS
3.       如果xm-1≠yn-1,那么当zk-1≠yn-1ZYn-1XLCS

下面简单证明一下这些性质:

1.       如果zk-1≠xm-1,那么我们可以把xm-1yn-1)加到Z中得到Z’,这样就得到XY的一个长度为k+1的公共子串Z’。这就与长度为kZXYLCS相矛盾了。因此一定有zk-1=xm-1=yn-1

既然zk-1=xm-1=yn-1,那如果我们删除zk-1xm-1yn-1)得到的Zk-1Xm-1Yn-1,显然Zk-1Xm-1Yn-1的一个公共子串,现在我们证明Zk-1Xm-1Yn-1LCS。用反证法不难证明。假设有Xm-1Yn-1有一个长度超过k-1的公共子串W,那么我们把加到W中得到W’,那W’就是XY的公共子串,并且长度超过k,这就和已知条件相矛盾了。

2.       还是用反证法证明。假设Z不是Xm-1YLCS,则存在一个长度超过kWXm-1YLCS,那W肯定也XY的公共子串,而已知条件中XY的公共子串的最大长度为k。矛盾。

3.       证明同2

有了上面的性质,我们可以得出如下的思路:求两字符串Xm={x0, x1,…xm-1}Yn={y0,y1,…,yn-1}LCS,如果xm-1=yn-1,那么只需求得Xm-1Yn-1LCS,并在其后添加xm-1yn-1)即可;如果xm-1≠yn-1,我们分别求得Xm-1YLCSYn-1XLCS,并且这两个LCS中较长的一个为XYLCS

如果我们记字符串XiYjLCS的长度为c[i,j],我们可以递归地求c[i,j]

          /      0                               if i<0 or j<0
c[i,j]=          c[i-1,j-1]+1                    if i,j>=0 and xi=x
j
         \       max(c[i,j-1],c[i-1,j]           if i,j>=0 and xi≠xj

上面的公式用递归函数不难求得。但从前面求Fibonacci第n项(本面试题系列第16题)的分析中我们知道直接递归会有很多重复计算,我们用从底向上循环求解的思路效率更高。

为了能够采用循环求解的思路,我们用一个矩阵(参考代码中的LCS_length)保存下来当前已经计算好了的c[i,j],当后面的计算需要这些数据时就可以直接从矩阵读取。另外,求取c[i,j]可以从c[i-1,j-1] c[i,j-1]或者c[i-1,j]三个方向计算得到,相当于在矩阵LCS_length中是从c[i-1,j-1]c[i,j-1]或者c[i-1,j]的某一个各自移动到c[i,j],因此在矩阵中有三种不同的移动方向:向左、向上和向左上方,其中只有向左上方移动时才表明找到LCS中的一个字符。于是我们需要用另外一个矩阵(参考代码中的LCS_direction)保存移动的方向。

参考代码如下:

#include "string.h"

// directions of LCS generation
enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp};

/////////////////////////////////////////////////////////////////////////////
// Get the length of two strings' LCSs, and print one of the LCSs
// Input: pStr1         - the first string
//        pStr2         - the second string
// Output: the length of two strings' LCSs
/////////////////////////////////////////////////////////////////////////////
int LCS(char* pStr1, char* pStr2)
{
      if(!pStr1 || !pStr2)
            return 0;

     
size_t length1 = strlen(pStr1);
      size_t length2 = strlen(pStr2);
      if(!length1 || !length2)
            return 0;

     
size_t i, j;

     
// initiate the length matrix
      int **LCS_length;
      LCS_length = (int**)(new int[length1]);
      for(i = 0; i < length1; ++ i)
            LCS_length[i] = (int*)new int[length2];

      for(i = 0; i < length1; ++ i)
            for(j = 0; j < length2; ++ j)
                  LCS_length[i][j] = 0;

 

      // initiate the direction matrix
      int **LCS_direction;
      LCS_direction = (int**)(new int[length1]);
      for( i = 0; i < length1; ++ i)
            LCS_direction[i] = (int*)new int[length2];

      for(i = 0; i < length1; ++ i)
            for(j = 0; j < length2; ++ j)
                  LCS_direction[i][j] = kInit;

      for(i = 0; i < length1; ++ i)
      {
            for(j = 0; j < length2; ++ j)
            {
                  if(i == 0 || j == 0)
                  {
                        if(pStr1[i] == pStr2[j])
                        {
                              LCS_length[i][j] = 1;
                              LCS_direction[i][j] = kLeftUp;
                        }
                        else
                              LCS_length[i][j] = 0;
                  }
                  // a char of LCS is found, 
                  // it comes from the left up entry in the direction matrix
                  else if(pStr1[i] == pStr2[j])
                  {
                        LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1;
                        LCS_direction[i][j] = kLeftUp;
                  }
                  // it comes from the up entry in the direction matrix
                  else if(LCS_length[i - 1][j] > LCS_length[i][j - 1])
                  {
                        LCS_length[i][j] = LCS_length[i - 1][j];
                        LCS_direction[i][j] = kUp;
                  }
                  // it comes from the left entry in the direction matrix
                  else
                  {
                        LCS_length[i][j] = LCS_length[i][j - 1];
                        LCS_direction[i][j] = kLeft;
                  }
            }
      }
      LCS_Print(LCS_direction, pStr1, pStr2, length1 - 1, length2 - 1);

      return LCS_length[length1 - 1][length2 - 1];
}

 

/////////////////////////////////////////////////////////////////////////////
// Print a LCS for two strings
// Input: LCS_direction - a 2d matrix which records the direction of
//                        LCS generation
//        pStr1         - the first string
//        pStr2         - the second string
//        row           - the row index in the matrix LCS_direction
//        col           - the column index in the matrix LCS_direction
/////////////////////////////////////////////////////////////////////////////
void LCS_Print(int **LCS_direction, 
                    char* pStr1, char* pStr2, 
                    size_t row, size_t col)
{
      if(pStr1 == NULL || pStr2 == NULL)
            return;

      size_t length1 = strlen(pStr1);
      size_t length2 = strlen(pStr2);

     
if(length1 == 0 || length2 == 0 || !(row < length1 && col < length2))
            return;

      // kLeftUp implies a char in the LCS is found
      if(LCS_direction[row][col] == kLeftUp)
      {
            if(row > 0 && col > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col - 1);

           
// print the char
            printf("%c", pStr1[row]);
      }
      else if(LCS_direction[row][col] == kLeft)
      {
            // move to the left entry in the direction matrix
            if(col > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row, col - 1);
      }
      else if(LCS_direction[row][col] == kUp)
      {
            // move to the up entry in the direction matrix
            if(row > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col);
      }
}

扩展:如果题目改成求两个字符串的最长公共子字符串,应该怎么求?子字符串的定义和子串的定义类似,但要求是连续分布在其他字符串中。比如输入两个字符串BDCABAABCBDAB的最长公共字符串有BDAB,它们的长度都是2

分享到:
评论

相关推荐

    用定长顺序存储结构表示串,求两个串的全部最长公共子串

    在本题目中,任务是使用定长顺序存储结构表示串,并找出两个字符串的最长公共子串。这是一个典型的字符串处理问题,通常在计算机科学和编程领域出现。以下是对这个任务的详细解析: 首先,我们需要理解“定长顺序...

    查找最长公共子串

    本话题聚焦于一个经典的算法问题——“查找最长公共子串”。这是一道典型的字符串算法题,主要考察对字符串操作和动态规划的理解。 首先,我们需要明确什么是“公共子串”。如果一个字符串s是另一个字符串t的子串,...

    最长公共子串的快速搜索算法

    最长公共子串的快速搜索算法 最长公共子串的快速搜索算法 最长公共子串的快速搜索算法

    动态规划——最长公共子序列和最长公共子串之Python实现

    用Python实现动态规划中最长公共子序列和最长公共子串问题!

    最长公共子串的求解问题

    最长公共子串求解,有需要的可以下来参考……

    C#最长公共子串(连续)算法(自创)

    在编程领域,最长公共子串(Longest Common Substring,LCS)问题是一个经典的问题,主要涉及字符串处理和算法设计。在这个案例中,我们关注的是一个由C#实现的自创算法来解决这个问题。下面将详细讲解这个算法的...

    新建 WinRAR 压缩文件_最长公共子串_

    在IT领域,尤其是在编程和算法设计中,"最长公共子串"是一个重要的概念。这个知识点主要涉及字符串处理和算法分析,对于计算机科学的学习者和开发者来说具有基础且实用的价值。在给定的压缩包文件中,我们可以看到它...

    最长公共子串MFC实现

    最长公共子串(Longest Common Substring,LCS)是一个在计算机科学中常见的字符串处理问题,它涉及到查找两个或多个字符串中的最长连续子序列,这个子序列同时存在于所有字符串中。MFC,全称为Microsoft Foundation...

    C语言求两个字符串的最长公共子串

    本文实例讲述了C语言求两个字符串的最长公共子串的方法。分享给大家供大家参考。具体实现方法如下: #include "stdio.h" #include "string.h" #include "stdlib.h" void getCommon(char str1[],char str2[],char * ...

    JavaScript自定义函数实现查找两个字符串最长公共子串的方法

    本文实例讲述了JavaScript自定义函数实现查找两个字符串最长公共子串的方法。分享给大家供大家参考,具体如下: //查找两个字符串的最长公共子串 function findSubStr(s1,s2){ var S=sstr= ,L1=s1.length,L2=s2....

    大整数计算器最长公共子串数据结构课设

    大整数计算器最长公共子串数据结构课设,沈阳工程学院

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

    在解决最长公共子串问题时,动态规划是计算机科学领域内一种广泛应用的算法策略。动态规划的核心在于将复杂问题分解为更小的子问题,并将子问题的答案存储起来,避免重复计算,最终解决整个问题。最长公共子串问题...

    动态规划:最长公共子串 LCS

    ### 动态规划:最长公共子串 LCS #### 长度与定义 **最长公共子串(Longest Common Substring, LCS)**是两个或多个字符串中的最长字符串,该字符串同时也是这些字符串的子串。这里需要注意区分子串与子序列的概念...

    获取最长公共子串

    在编程领域,"获取最长公共子串"是一个经典的问题,主要涉及到字符串处理和算法设计。这个问题的基本目标是从两个或多个给定的字符串中找到最长的共同连续子序列,即这个子序列是每个字符串的一部分,且在原字符串中...

    求解最大子序列、最长递增子序列、最长公共子串、最长公共子序列

    求解最大子序列、最长递增子序列、最长公共子串、最长公共子序列. http://blog.csdn.net/ssuchange/article/details/17341693

    java实现求两个字符串最长公共子串的方法

    在编程领域,求解两个字符串的最长公共子串是一个经典问题,主要应用于文本处理、比较和搜索算法。这里我们将深入探讨如何使用Java实现这一方法,同时结合华为在线判题平台(OJ)的要求来编写代码。 首先,我们需要...

    PHP实现求两个字符串最长公共子串的方法示例

    在编程领域,最长公共子串(Longest Common Substring,LCS)问题是一个经典的问题,它寻找两个或多个字符串中的最长连续子序列,这个子序列同时存在于所有字符串中。在这个问题中,我们专注于PHP如何解决两个字符串...

    编辑距离与最长公共子串(字符串的相似度)

    用本程序可得到字符串的相似度和字符串的公共子串以及编辑距离。

    LCS最长公共子串

    c源码编写的求两个字符串的最长公共子串,采用递归算法

Global site tag (gtag.js) - Google Analytics