📜  最长公共子串 | DP-29

📅  最后修改于: 2021-09-17 07:03:32             🧑  作者: Mango

给定两个字符串“X”和“Y”,找出最长公共子串的长度。

例子 :

最长公共子串

方法:
设 m 和 n 分别是第一个和第二个字符串的长度。

一个简单的解决方案是逐一考虑第一个字符串的所有子字符串,并为每个子字符串检查它是否是第二个字符串的子字符串。跟踪最大长度子串。将有 O(m^2) 个子串,我们可以在 O(n) 时间内找到一个字符串是否是另一个字符串上的子串(参见这里)。所以这个方法的整体时间复杂度为 O(n * m 2 )

动态规划可用于在 O(m*n) 时间内找到最长的公共子串。这个想法是找到两个字符串的所有子字符串的最长公共后缀的长度,并将这些长度存储在一个表中。

以下是上述解决方案的迭代实现。

C++
/* Dynamic Programming solution to
   find length of the
   longest common substring */
#include 
#include 
using namespace std;
 
/* Returns length of longest
   common substring of X[0..m-1]
   and Y[0..n-1] */
int LCSubStr(char* X, char* Y, int m, int n)
{
    // Create a table to store
    // lengths of longest
    // common suffixes of substrings.  
    // Note that LCSuff[i][j] contains
    // length of longest common suffix
    // of X[0..i-1] and Y[0..j-1].
 
    int LCSuff[m + 1][n + 1];
    int result = 0; // To store length of the
                    // longest common substring
 
    /* Following steps build LCSuff[m+1][n+1] in
        bottom up fashion. */
    for (int i = 0; i <= m; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            // The first row and first column
            // entries have no logical meaning,
            // they are used only for simplicity
            // of program
            if (i == 0 || j == 0)
                LCSuff[i][j] = 0;
 
            else if (X[i - 1] == Y[j - 1]) {
                LCSuff[i][j] = LCSuff[i - 1][j - 1] + 1;
                result = max(result, LCSuff[i][j]);
            }
            else
                LCSuff[i][j] = 0;
        }
    }
    return result;
}
 
// Driver code
int main()
{
    char X[] = "OldSite:GeeksforGeeks.org";
    char Y[] = "NewSite:GeeksQuiz.com";
 
    int m = strlen(X);
    int n = strlen(Y);
 
    cout << "Length of Longest Common Substring is "
         << LCSubStr(X, Y, m, n);
    return 0;
}


Java
//  Java implementation of
// finding length of longest
// Common substring using
// Dynamic Programming
class GFG {
    /*
       Returns length of longest common substring
       of X[0..m-1] and Y[0..n-1]
    */
    static int LCSubStr(char X[], char Y[],
                         int m, int n)
    {
        // Create a table to store
        // lengths of longest common
        // suffixes of substrings.
        // Note that LCSuff[i][j]
        // contains length of longest
        // common suffix of
        // X[0..i-1] and Y[0..j-1].
        // The first row and first
        // column entries have no
        // logical meaning, they are
        // used only for simplicity of program
        int LCStuff[][] = new int[m + 1][n + 1];
       
        // To store length of the longest
        // common substring
        int result = 0;
 
        // Following steps build
        // LCSuff[m+1][n+1] in bottom up fashion
        for (int i = 0; i <= m; i++)
        {
            for (int j = 0; j <= n; j++)
            {
                if (i == 0 || j == 0)
                    LCStuff[i][j] = 0;
                else if (X[i - 1] == Y[j - 1])
                {
                    LCStuff[i][j]
                        = LCStuff[i - 1][j - 1] + 1;
                    result = Integer.max(result,
                                         LCStuff[i][j]);
                }
                else
                    LCStuff[i][j] = 0;
            }
        }
        return result;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        String X = "OldSite:GeeksforGeeks.org";
        String Y = "NewSite:GeeksQuiz.com";
 
        int m = X.length();
        int n = Y.length();
 
        System.out.println(LCSubStr(X.toCharArray(),
                                    Y.toCharArray(), m,
                       n));
    }
}
 
// This code is contributed by Sumit Ghosh


Python3
# Python3 implementation of Finding
# Length of Longest Common Substring
 
# Returns length of longest common
# substring of X[0..m-1] and Y[0..n-1]
 
 
def LCSubStr(X, Y, m, n):
 
    # Create a table to store lengths of
    # longest common suffixes of substrings.
    # Note that LCSuff[i][j] contains the
    # length of longest common suffix of
    # X[0...i-1] and Y[0...j-1]. The first
    # row and first column entries have no
    # logical meaning, they are used only
    # for simplicity of the program.
 
    # LCSuff is the table with zero
    # value initially in each cell
    LCSuff = [[0 for k in range(n+1)] for l in range(m+1)]
 
    # To store the length of
    # longest common substring
    result = 0
 
    # Following steps to build
    # LCSuff[m+1][n+1] in bottom up fashion
    for i in range(m + 1):
        for j in range(n + 1):
            if (i == 0 or j == 0):
                LCSuff[i][j] = 0
            elif (X[i-1] == Y[j-1]):
                LCSuff[i][j] = LCSuff[i-1][j-1] + 1
                result = max(result, LCSuff[i][j])
            else:
                LCSuff[i][j] = 0
    return result
 
 
# Driver Code
X = 'OldSite:GeeksforGeeks.org'
Y = 'NewSite:GeeksQuiz.com'
 
m = len(X)
n = len(Y)
 
print('Length of Longest Common Substring is',
      LCSubStr(X, Y, m, n))
 
# This code is contributed by Soumen Ghosh


C#
// C# implementation of finding length of longest
// Common substring using Dynamic Programming
using System;
 
class GFG {
 
    // Returns length of longest common
    // substring of X[0..m-1] and Y[0..n-1]
    static int LCSubStr(string X, string Y, int m, int n)
    {
 
        // Create a table to store lengths of
        // longest common suffixes of substrings.
        // Note that LCSuff[i][j] contains length
        // of longest common suffix of X[0..i-1]
        // and Y[0..j-1]. The first row and first
        // column entries have no logical meaning,
        // they are used only for simplicity of
        // program
        int[, ] LCStuff = new int[m + 1, n + 1];
 
        // To store length of the longest common
        // substring
        int result = 0;
 
        // Following steps build LCSuff[m+1][n+1]
        // in bottom up fashion
        for (int i = 0; i <= m; i++)
        {
            for (int j = 0; j <= n; j++)
            {
                if (i == 0 || j == 0)
                    LCStuff[i, j] = 0;
                else if (X[i - 1] == Y[j - 1])
                {
                    LCStuff[i, j]
                        = LCStuff[i - 1, j - 1] + 1;
 
                    result
                        = Math.Max(result, LCStuff[i, j]);
                }
                else
                    LCStuff[i, j] = 0;
            }
        }
 
        return result;
    }
 
    // Driver Code
    public static void Main()
    {
        String X = "OldSite:GeeksforGeeks.org";
        String Y = "NewSite:GeeksQuiz.com";
 
        int m = X.Length;
        int n = Y.Length;
 
        Console.Write("Length of Longest Common"
                      + " Substring is "
                      + LCSubStr(X, Y, m, n));
    }
}
 
// This code is contributed by Sam007.


PHP


Javascript


Java
// Java implementation of the above approach
 
class GFG
{
   
    // Function to find the length of the
    // longest LCS
    static int LCSubStr(String s,String t,
                        int n,int m)
    { 
       
        // Create DP table
        int dp[][]=new int[2][m+1];
        int res=0;
      
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(s.charAt(i-1)==t.charAt(j-1))
                {
                    dp[i%2][j]=dp[(i-1)%2][j-1]+1;
                    if(dp[i%2][j]>res)
                        res=dp[i%2][j];
                }
                else dp[i%2][j]=0;
            }
        }
        return res;
    }
   
    // Driver Code
    public static void main (String[] args)
    {
        String X="OldSite:GeeksforGeeks.org";
        String Y="NewSite:GeeksQuiz.com";
         
        int m=X.length();
        int n=Y.length();
         
        // Function call
        System.out.println(LCSubStr(X,Y,m,n));
         
    }
}


Python3
# Python implementation of the above approach
 
# Function to find the length of the
# longest LCS
def LCSubStr(s, t, n, m):
   
    # Create DP table
    dp = [[0 for i in range(m + 1)] for j in range(2)]
    res = 0
     
    for i in range(1,n + 1):
        for j in range(1,m + 1):
            if(s[i - 1] == t[j - 1]):
                dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1
                if(dp[i % 2][j] > res):
                    res = dp[i % 2][j]
            else:
                dp[i % 2][j] = 0
    return res
 
# Driver Code
X = "OldSite:GeeksforGeeks.org"
Y = "NewSite:GeeksQuiz.com"
m = len(X)
n = len(Y)
 
# Function call
print(LCSubStr(X,Y,m,n))
 
# This code is contributed by avanitrachhadiya2155


C#
// C# implementation of the above approach
using System;
public class GFG
{
 
  // Function to find the length of the
  // longest LCS
  static int LCSubStr(string s,string t,
                      int n,int m)
  { 
 
    // Create DP table
    int[,] dp = new int[2, m + 1];
    int res = 0;
 
    for(int i = 1; i <= n; i++)
    {
      for(int j = 1; j <= m; j++)
      {
        if(s[i - 1] == t[j - 1])
        {
          dp[i % 2, j] = dp[(i - 1) % 2, j - 1] + 1;
          if(dp[i % 2, j] > res)
            res = dp[i % 2, j];
        }
        else dp[i % 2, j] = 0;
      }
    }
    return res;
  }
 
  // Driver Code
  static public void Main (){
    string X = "OldSite:GeeksforGeeks.org";
    string Y = "NewSite:GeeksQuiz.com";
 
    int m = X.Length;
    int n = Y.Length;
 
    // Function call
    Console.WriteLine(LCSubStr(X,Y,m,n));
  }
}
 
// This code is contributed by rag2127


C++
// C++ program using to find length of the
// longest common substring  recursion
#include 
 
using namespace std;
 
string X, Y;
 
// Returns length of function f
// or longest common substring
// of X[0..m-1] and Y[0..n-1]
int lcs(int i, int j, int count)
{
 
    if (i == 0 || j == 0)
        return count;
 
    if (X[i - 1] == Y[j - 1]) {
        count = lcs(i - 1, j - 1, count + 1);
    }
    count = max(count,
                max(lcs(i, j - 1, 0),
                    lcs(i - 1, j, 0)));
    return count;
}
 
// Driver code
int main()
{
    int n, m;
 
    X = "abcdxyz";
    Y = "xyzabcd";
 
    n = X.size();
    m = Y.size();
 
    cout << lcs(n, m, 0);
 
    return 0;
}


Java
// Java program using to find length of the
// longest common substring recursion
 
class GFG {
 
    static String X, Y;
    // Returns length of function
    // for longest common
    // substring of X[0..m-1] and Y[0..n-1]
    static int lcs(int i, int j, int count)
    {
 
        if (i == 0 || j == 0)
        {
            return count;
        }
 
        if (X.charAt(i - 1)
            == Y.charAt(j - 1))
        {
            count = lcs(i - 1, j - 1, count + 1);
        }
        count = Math.max(count,
                         Math.max(lcs(i, j - 1, 0),
                                  lcs(i - 1, j, 0)));
        return count;
    }
     
    // Driver code
    public static void main(String[] args)
    {
        int n, m;
        X = "abcdxyz";
        Y = "xyzabcd";
 
        n = X.length();
        m = Y.length();
 
        System.out.println(lcs(n, m, 0));
    }
}
// This code is contributed by Rajput-JI


Python3
# Python3 program using to find length of
# the longest common substring recursion
 
# Returns length of function for longest
# common substring of X[0..m-1] and Y[0..n-1]
 
 
def lcs(i, j, count):
 
    if (i == 0 or j == 0):
        return count
 
    if (X[i - 1] == Y[j - 1]):
        count = lcs(i - 1, j - 1, count + 1)
 
    count = max(count, max(lcs(i, j - 1, 0),
                           lcs(i - 1, j, 0)))
 
    return count
 
 
# Driver code
if __name__ == "__main__":
 
    X = "abcdxyz"
    Y = "xyzabcd"
 
    n = len(X)
    m = len(Y)
 
    print(lcs(n, m, 0))
 
# This code is contributed by Ryuga


C#
// C# program using to find length
// of the longest common substring
// recursion
using System;
 
class GFG {
    static String X, Y;
 
    // Returns length of function for
    // longest common substring of
    // X[0..m-1] and Y[0..n-1]
    static int lcs(int i, int j, int count)
    {
 
        if (i == 0 || j == 0) {
            return count;
        }
 
        if (X[i - 1] == Y[j - 1]) {
            count = lcs(i - 1, j - 1, count + 1);
        }
        count = Math.Max(count, Math.Max(lcs(i, j - 1, 0),
                                         lcs(i - 1, j, 0)));
        return count;
    }
 
    // Driver code
    public static void Main()
    {
        int n, m;
        X = "abcdxyz";
        Y = "xyzabcd";
 
        n = X.Length;
        m = Y.Length;
 
        Console.Write(lcs(n, m, 0));
    }
}
 
// This code is contributed by Rajput-JI


PHP


Javascript


Python3
# Python code for the above approach
from functools import lru_cache
from operator import itemgetter
 
def longest_common_substring(x: str, y: str) -> (int, int, int):
     
    # function to find the longest common substring
 
    # Memorizing with maximum size of the memory as 1
    @lru_cache(maxsize=1) 
     
    # function to find the longest common prefix
    def longest_common_prefix(i: int, j: int) -> int:
       
        if 0 <= i < len(x) and 0 <= j < len(y) and x[i] == y[j]:
            return 1 + longest_common_prefix(i + 1, j + 1)
        else:
            return 0
 
    # digonally computing the subproplems
    # to decrease memory dependency
    def digonal_computation():
         
        # upper right triangle of the 2D array
        for k in range(len(x)):       
            yield from ((longest_common_prefix(i, j), i, j)
                        for i, j in zip(range(k, -1, -1),
                                    range(len(y) - 1, -1, -1)))
         
        # lower left triangle of the 2D array
        for k in range(len(y)):       
            yield from ((longest_common_prefix(i, j), i, j)
                        for i, j in zip(range(k, -1, -1),
                                    range(len(x) - 1, -1, -1)))
 
    # returning the maximum of all the subproblems
    return max(digonal_computation(), key=itemgetter(0), default=(0, 0, 0))
 
# Driver Code
if __name__ == '__main__':
    x: str = 'GeeksforGeeks'
    y: str = 'GeeksQuiz'
    length, i, j = longest_common_substring(x, y)
    print(f'length: {length}, i: {i}, j: {j}')
    print(f'x substring: {x[i: i + length]}')
    print(f'y substring: {y[j: j + length]}')


输出
Length of Longest Common Substring is 10

时间复杂度: O(m*n)
辅助空间: O(m*n)

另一种方法:(空间优化方法)。
在上面的方法中,我们只使用了二维数组的最后一行,因此我们可以通过使用来优化空间
维度为 2*(min(n,m)) 的二维数组。

下面是上述方法的实现:

Java

// Java implementation of the above approach
 
class GFG
{
   
    // Function to find the length of the
    // longest LCS
    static int LCSubStr(String s,String t,
                        int n,int m)
    { 
       
        // Create DP table
        int dp[][]=new int[2][m+1];
        int res=0;
      
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(s.charAt(i-1)==t.charAt(j-1))
                {
                    dp[i%2][j]=dp[(i-1)%2][j-1]+1;
                    if(dp[i%2][j]>res)
                        res=dp[i%2][j];
                }
                else dp[i%2][j]=0;
            }
        }
        return res;
    }
   
    // Driver Code
    public static void main (String[] args)
    {
        String X="OldSite:GeeksforGeeks.org";
        String Y="NewSite:GeeksQuiz.com";
         
        int m=X.length();
        int n=Y.length();
         
        // Function call
        System.out.println(LCSubStr(X,Y,m,n));
         
    }
}

蟒蛇3

# Python implementation of the above approach
 
# Function to find the length of the
# longest LCS
def LCSubStr(s, t, n, m):
   
    # Create DP table
    dp = [[0 for i in range(m + 1)] for j in range(2)]
    res = 0
     
    for i in range(1,n + 1):
        for j in range(1,m + 1):
            if(s[i - 1] == t[j - 1]):
                dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1
                if(dp[i % 2][j] > res):
                    res = dp[i % 2][j]
            else:
                dp[i % 2][j] = 0
    return res
 
# Driver Code
X = "OldSite:GeeksforGeeks.org"
Y = "NewSite:GeeksQuiz.com"
m = len(X)
n = len(Y)
 
# Function call
print(LCSubStr(X,Y,m,n))
 
# This code is contributed by avanitrachhadiya2155

C#

// C# implementation of the above approach
using System;
public class GFG
{
 
  // Function to find the length of the
  // longest LCS
  static int LCSubStr(string s,string t,
                      int n,int m)
  { 
 
    // Create DP table
    int[,] dp = new int[2, m + 1];
    int res = 0;
 
    for(int i = 1; i <= n; i++)
    {
      for(int j = 1; j <= m; j++)
      {
        if(s[i - 1] == t[j - 1])
        {
          dp[i % 2, j] = dp[(i - 1) % 2, j - 1] + 1;
          if(dp[i % 2, j] > res)
            res = dp[i % 2, j];
        }
        else dp[i % 2, j] = 0;
      }
    }
    return res;
  }
 
  // Driver Code
  static public void Main (){
    string X = "OldSite:GeeksforGeeks.org";
    string Y = "NewSite:GeeksQuiz.com";
 
    int m = X.Length;
    int n = Y.Length;
 
    // Function call
    Console.WriteLine(LCSubStr(X,Y,m,n));
  }
}
 
// This code is contributed by rag2127
输出
10

时间复杂度: O(n*m)
辅助空间: O(min(m,n))

另一种方法:(使用递归)
这是上述方法的递归解决方案。

C++

// C++ program using to find length of the
// longest common substring  recursion
#include 
 
using namespace std;
 
string X, Y;
 
// Returns length of function f
// or longest common substring
// of X[0..m-1] and Y[0..n-1]
int lcs(int i, int j, int count)
{
 
    if (i == 0 || j == 0)
        return count;
 
    if (X[i - 1] == Y[j - 1]) {
        count = lcs(i - 1, j - 1, count + 1);
    }
    count = max(count,
                max(lcs(i, j - 1, 0),
                    lcs(i - 1, j, 0)));
    return count;
}
 
// Driver code
int main()
{
    int n, m;
 
    X = "abcdxyz";
    Y = "xyzabcd";
 
    n = X.size();
    m = Y.size();
 
    cout << lcs(n, m, 0);
 
    return 0;
}

Java

// Java program using to find length of the
// longest common substring recursion
 
class GFG {
 
    static String X, Y;
    // Returns length of function
    // for longest common
    // substring of X[0..m-1] and Y[0..n-1]
    static int lcs(int i, int j, int count)
    {
 
        if (i == 0 || j == 0)
        {
            return count;
        }
 
        if (X.charAt(i - 1)
            == Y.charAt(j - 1))
        {
            count = lcs(i - 1, j - 1, count + 1);
        }
        count = Math.max(count,
                         Math.max(lcs(i, j - 1, 0),
                                  lcs(i - 1, j, 0)));
        return count;
    }
     
    // Driver code
    public static void main(String[] args)
    {
        int n, m;
        X = "abcdxyz";
        Y = "xyzabcd";
 
        n = X.length();
        m = Y.length();
 
        System.out.println(lcs(n, m, 0));
    }
}
// This code is contributed by Rajput-JI

蟒蛇3

# Python3 program using to find length of
# the longest common substring recursion
 
# Returns length of function for longest
# common substring of X[0..m-1] and Y[0..n-1]
 
 
def lcs(i, j, count):
 
    if (i == 0 or j == 0):
        return count
 
    if (X[i - 1] == Y[j - 1]):
        count = lcs(i - 1, j - 1, count + 1)
 
    count = max(count, max(lcs(i, j - 1, 0),
                           lcs(i - 1, j, 0)))
 
    return count
 
 
# Driver code
if __name__ == "__main__":
 
    X = "abcdxyz"
    Y = "xyzabcd"
 
    n = len(X)
    m = len(Y)
 
    print(lcs(n, m, 0))
 
# This code is contributed by Ryuga

C#

// C# program using to find length
// of the longest common substring
// recursion
using System;
 
class GFG {
    static String X, Y;
 
    // Returns length of function for
    // longest common substring of
    // X[0..m-1] and Y[0..n-1]
    static int lcs(int i, int j, int count)
    {
 
        if (i == 0 || j == 0) {
            return count;
        }
 
        if (X[i - 1] == Y[j - 1]) {
            count = lcs(i - 1, j - 1, count + 1);
        }
        count = Math.Max(count, Math.Max(lcs(i, j - 1, 0),
                                         lcs(i - 1, j, 0)));
        return count;
    }
 
    // Driver code
    public static void Main()
    {
        int n, m;
        X = "abcdxyz";
        Y = "xyzabcd";
 
        n = X.Length;
        m = Y.Length;
 
        Console.Write(lcs(n, m, 0));
    }
}
 
// This code is contributed by Rajput-JI

PHP


Javascript


输出
4

最大空间优化

  1. 在这种方法中,我们将使用递归来查找所有可能子串的最长前缀。
  2. LongestCommonSuffix\left(i, j\right)              给出分别从字符串X , Y 的索引i , j开始的最长公共后缀的长度。
  3. 那么函数可以定义为:
    LongestCommonSuffix\left(i, j\right) = \begin{cases} 1 + LongestCommonSuffix\left(i + 1, j + 1\right) & \text{if} & 0 \le i \lt \left|X\right| & \text{and} & 0 \le j \lt \left|Y\right| & \text{and} & X\left[i\right] = Y\left[j\right] \\ 0 & \text{otherwise} \end{cases}
  4. 在这个递归中,我们可以看到该函数只有一个依赖项,这意味着如果我们按特定顺序进行计算,我们可以只记住之前的计算。
  5. 考虑下表,我们记住了解决方案:
  0 1 2 3 4
0          
1          
2          
3          

我们需要向上对角地找到解决方案。在这个特定的例子中:

  • 第一对角线
    • (4, 0)
  • 第二对角线
    • (4, 1)
    • (3, 0)
  • 第三对角线
    • (4, 2)
    • (3, 1)
    • (2, 0)

像这样,我们只需要记住之前的计算。

蟒蛇3

# Python code for the above approach
from functools import lru_cache
from operator import itemgetter
 
def longest_common_substring(x: str, y: str) -> (int, int, int):
     
    # function to find the longest common substring
 
    # Memorizing with maximum size of the memory as 1
    @lru_cache(maxsize=1) 
     
    # function to find the longest common prefix
    def longest_common_prefix(i: int, j: int) -> int:
       
        if 0 <= i < len(x) and 0 <= j < len(y) and x[i] == y[j]:
            return 1 + longest_common_prefix(i + 1, j + 1)
        else:
            return 0
 
    # digonally computing the subproplems
    # to decrease memory dependency
    def digonal_computation():
         
        # upper right triangle of the 2D array
        for k in range(len(x)):       
            yield from ((longest_common_prefix(i, j), i, j)
                        for i, j in zip(range(k, -1, -1),
                                    range(len(y) - 1, -1, -1)))
         
        # lower left triangle of the 2D array
        for k in range(len(y)):       
            yield from ((longest_common_prefix(i, j), i, j)
                        for i, j in zip(range(k, -1, -1),
                                    range(len(x) - 1, -1, -1)))
 
    # returning the maximum of all the subproblems
    return max(digonal_computation(), key=itemgetter(0), default=(0, 0, 0))
 
# Driver Code
if __name__ == '__main__':
    x: str = 'GeeksforGeeks'
    y: str = 'GeeksQuiz'
    length, i, j = longest_common_substring(x, y)
    print(f'length: {length}, i: {i}, j: {j}')
    print(f'x substring: {x[i: i + length]}')
    print(f'y substring: {y[j: j + length]}')
输出
length: 5, i: 0, j: 0
x substring: Geeks
y substring: Geeks

时间复杂度O\left(\left|X\right|\left|Y\right|\right)

空间复杂度O\left(1\right)

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程