最长公共子串问题
题目:给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子字符串
一个字符串的子字符串是指从该字符串中截取的某个个连续片段。
例如,“abc” 是 “abcde” 的子字符串,但 “abd” 不是 “abcde” 的子字符串。两个字符串公共子字符串是这两个字符串共有的子字符串。
这个题目可以用两种方法来求解
滑动窗口法
为了了解求解的过程,我们先来看看下面几个图,可以清晰地看出滑动窗口解法是怎么求解的,看懂了图片求解的过程,之后的编程就很简单了,只是把图片上面的思维转化成程序的语言就行
有两个字符串sdfghasdfrtbn和fghnad
我们来从图中看看滑动窗口方法是怎么工作的
- 从图中可以看出解题的基本思路,剩下的就是把思路转化为代码
def get_longest_comment_sub_string(s1: str, s2: str):
s1_len = len(s1)
s2_len = len(s2)
# 确定长字符串,短串
(long_str, shot_str, max_len, min_len) = (s1, s2, s1_len, s2_len) if s1_len > s2_len else (s2, s1, s2_len, s1_len)
# 记录最长子串长度
max_count = 0
# 记录最长子串
sub_str = None
# 记录匹配的长度
match_count = 0
# 窗口从长串开始滑动
for i in range(max_len):
# 计算长串剩余的长度
left_len = max_len - i
# 确定需要计数的长度,防止列表越界
count_len = min(min_len, left_len)
for j in range(count_len):
if long_str[i + j] == shot_str[j]:
match_count += 1
if match_count > max_count:
# 记录长度
max_count = match_count
# 记录子串
sub_str = shot_str[j + 1- match_count : j + 1]
else:
# 记录归0
match_count = 0
# 这里要归0 很重要(如果这层循环尾部字符正好相同,不归0会出BUG)
match_count = 0
return sub_str
动态规划法
动态规划的方法我们也可以用画图的方式来理解这个过程
- 从图中可以看出每次取出字符比较的时候如果字符相等,则该对应下标的数字等于其斜上方的数字+ 1
def dp_longest_comment_sub_string(s1: str, s2: str):
n = len(s1)
m = len(s2)
# 构建(m+1)*(n+1)的0矩阵
dp = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
# 记录最大长度
max_count = 0
# 记录最长子串
sub_str = None
for i in range(n):
for j in range(m):
if s1[i] == s2[j]:
# 记录此时的子串长度等于斜上方+1
dp[i + 1][j + 1] = dp[i][j] + 1
# 和最长长度比较
if dp[i + 1][j + 1] > max_count:
# 如果大于已知最长,则覆盖记录
max_count = dp[i + 1][j + 1]
sub_str = s1[i + 1 - max_count:i + 1]
return sub_str
总结
动态规划的方法在代码结构上要优于滑动窗口的方法,但是滑动窗口的方法便于理解
两者的时间复杂度都是
O
(
m
∗
n
)
O(m*n)
O(m∗n),,其中m,n为字符串长度
滑动窗口不需要开辟新的空间(记录子串不算在内),动态规划则需要开辟新的内存空间存储dp数组