给出两个字符串X和Y,任务是找出最长的共同子串的长度。
举例说明。
输入。X = "GeeksforGeeks", y = "GeeksQuiz" 输出。5
解释。最大的公共子串是 "Geeks",长度为5。输入。X = "abcdxyz", y = "xyzabcd" 输出。4
解释。最大的公共子串是 "abcd",长度为4。输入。X = "zxabcdezy", y = "yzabcdezx"
输出。6
解释。最长的公共子串是 "abcdez",长度为6。
使用动态编程解决最长共线的问题:
这个问题可以用动态编程在O(len(X) * len(Y))中解决,见此。在这篇文章中,我们将讨论一种有效的方法。
使用二进制搜索和滚动哈希的最长公共子串
先决条件:
观察一下。
如果两个字符串中都有一个长度为K的公共子串,那么就会有长度为0,1,...,K-1 的公共子串**。** 因此,可以应用答案的二进制搜索。
按照下面的步骤来实现这个想法。
- 最小可能的答案(low)=0,最大可能的答案(high)=min(len(X), len(Y)),二进制搜索的范围将是**[0, min(len(X), len(Y))]**。
- 对于每一个mid,检查是否存在 一个长度为mid的共同子串,如果存在则更新low*,* 否则更新high*。*
- 为了检查是否存在长度为K的公共子串,可以使用多项式滚动哈希函数。
- 遍历所有大小为K的窗口 字符串X 和字符串Y 并得到哈希值。
- 如果存在一个共同的哈希,则返回True,否则返回False。
下面是这个方法的实现。
Python3
# Python code to implement the approach
# Function to implement rolling hash
class ComputeHash:
# Generates hash in O(n(log(n)))
def __init__(self, s, p, mod):
n = len(s)
self.hash = [0] * n
self.inv_mod = [0] * n
self.mod = mod
self.p = p
p_pow = 1
hash_value = 0
for i in range(n):
c = ord(s[i]) - 65 + 1
hash_value = (hash_value + c * p_pow) % self.mod
self.hash[i] = hash_value
self.inv_mod[i] = pow(p_pow, self.mod - 2, self.mod)
p_pow = (p_pow * self.p) % self.mod
# Return hash of a window in O(1)
def get_hash(self, l, r):
if l == 0:
return self.hash[r]
window = (self.hash[r] - self.hash[l - 1]) % self.mod
return (window * self.inv_mod[l]) % self.mod
# Function to get the longest common substring
def longestCommonSubstr(X, Y, n, m):
p1, p2 = 31, 37
m1, m2 = pow(10, 9) + 9, pow(10, 9) + 7
# Initialize two hash objects
# with different p1, p2, m1, m2
# to reduce collision
hash_X1 = ComputeHash(X, p1, m1)
hash_X2 = ComputeHash(X, p2, m2)
hash_Y1 = ComputeHash(Y, p1, m1)
hash_Y2 = ComputeHash(Y, p2, m2)
# Function that returns the existence
# of a common substring of length k
def exists(k):
if k == 0:
return True
st = set()
# Iterate on X and get hash tuple
# of all windows of size k
for i in range(n - k + 1):
h1 = hash_X1.get_hash(i, i + k - 1)
h2 = hash_X2.get_hash(i, i + k - 1)
cur_window_hash = (h1, h2)
# Put the hash tuple in the set
st.add(cur_window_hash)
# Iterate on Y and get hash tuple
# of all windows of size k
for i in range(m - k + 1):
h1 = hash_Y1.get_hash(i, i + k - 1)
h2 = hash_Y2.get_hash(i, i + k - 1)
cur_window_hash = (h1, h2)
# If hash exists in st return True
if cur_window_hash in st:
return True
return False
# Binary Search on length
answer = 0
low, high = 0, min(n, m)
while low <= high:
mid = (low + high) // 2
if exists(mid):
answer = mid
low = mid + 1
else:
high = mid - 1
return answer
# Driver Code
if __name__ == '__main__':
X = 'GeeksforGeeks'
Y = 'GeeksQuiz'
print(longestCommonSubstr(X, Y, len(X), len(Y)))
输出
5
4
6
时间复杂度。O(n * log(m1)) + O(m * log((m1)) + O((n + m) * log(min(n, m))
- 生成哈希对象需要O(n*log(m1)),其中n是字符串的长度,m1 = pow(10, 9) + 7。
- 二进制搜索需要 O(log(min(n, m)),其中n, m是两个字符串的长度。
- 窗口的哈希值需要O(1)时间。
- 存在函数需要O(n+m)时间。
辅助空间。O(n + m)