给定两个字符串“X”和“Y”,找出最长公共子串的长度。
例子 :
Input : X = “GeeksforGeeks”, y = “GeeksQuiz”
Output : 5
Explanation:
The longest common substring is “Geeks” and is of length 5.
Input : X = “abcdxyz”, y = “xyzabcd”
Output : 4
Explanation:
The longest common substring is “abcd” and is of length 4.
Input : X = “zxabcdezy”, y = “yzabcdezx”
Output : 6
Explanation:
The longest common substring is “abcdez” and is of length 6.
方法:
设 m 和 n 分别是第一个和第二个字符串的长度。
一个简单的解决方案是逐一考虑第一个字符串的所有子字符串,并为每个子字符串检查它是否是第二个字符串的子字符串。跟踪最大长度子串。将有 O(m^2) 个子串,我们可以在 O(n) 时间内找到一个字符串是否是另一个字符串上的子串(参见这里)。所以这个方法的整体时间复杂度为 O(n * m 2 )
动态规划可用于在 O(m*n) 时间内找到最长的公共子串。这个想法是找到两个字符串的所有子字符串的最长公共后缀的长度,并将这些长度存储在一个表中。
The longest common suffix has following optimal substructure property.
If last characters match, then we reduce both lengths by 1
LCSuff(X, Y, m, n) = LCSuff(X, Y, m-1, n-1) + 1 if X[m-1] = Y[n-1]
If last characters do not match, then result is 0, i.e.,
LCSuff(X, Y, m, n) = 0 if (X[m-1] != Y[n-1])
Now we consider suffixes of different substrings ending at different indexes.
The maximum length Longest Common Suffix is the longest common substring.
LCSubStr(X, Y, m, n) = Max(LCSuff(X, Y, i, j)) where 1 <= i <= m and 1 <= j <= 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
最大空间优化:
- 在这种方法中,我们将使用递归来查找所有可能子串的最长前缀。
- 让给出分别从字符串X , Y 的索引i , j开始的最长公共后缀的长度。
- 那么函数可以定义为:
- 在这个递归中,我们可以看到该函数只有一个依赖项,这意味着如果我们按特定顺序进行计算,我们可以只记住之前的计算。
- 考虑下表,我们记住了解决方案:
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
时间复杂度:
空间复杂度:
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。