
1) 【一句话结论】计算两个字符串的最长公共子序列(LCS)问题,可通过动态规划解决,核心是定义前缀字符串的LCS长度状态,状态转移方程为匹配则加1、不匹配取最大值,时间复杂度O(mn),空间可优化为O(min(m,n))。
2) 【原理/概念讲解】最长公共子序列(LCS)是指两个序列中共同出现的字符序列(顺序不变,不必连续)。动态规划的核心是状态定义:设字符串s1长度为m,s2长度为n,定义dp[i][j]为s1前i个字符(i从1到m)和s2前j个字符(j从1到n)的LCS长度。
状态转移逻辑:
3) 【对比与适用场景】
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 | 注意点 |
|---|---|---|---|---|
| 标准动态规划 | O(mn) | O(mn) | m、n均较小(如≤1000) | 需m*n内存,可能超出限制 |
| 空间优化动态规划 | O(mn) | O(min(m,n)) | m或n很大(如m=10^5,n=10^3) | 交换字符串使较短为列,遍历时用prev保存上一行值 |
4) 【示例】
示例:s1 = "ABCBDAB",s2 = "BDCABA",求LCS。
def lcs(s1, s2):
m, n = len(s1), len(s2)
if m < n: s1, s2, m, n = s2, s1, n, m # 交换使s1更长,用s2作为列
dp = [0] * (n+1)
for i in range(1, m+1):
prev = 0
for j in range(1, n+1):
temp = dp[j] # 保存dp[i-1][j]
if s1[i-1] == s2[j-1]:
dp[j] = prev + 1
else:
dp[j] = max(dp[j], dp[j-1])
prev = temp
return dp[n]
5) 【面试口播版答案】
“面试官您好,计算两个字符串的最长公共子序列(LCS),我会用动态规划来解决。首先定义状态:设字符串s1长度为m,s2长度为n,dp[i][j]表示s1前i个字符和s2前j个字符的LCS长度。状态转移方程是:如果s1[i-1]等于s2[j-1],那么dp[i][j]等于dp[i-1][j-1]加1;否则取dp[i-1][j]和dp[i][j-1]的最大值。这样通过递推计算所有子问题的解,最终dp[m][n]就是结果。时间复杂度是O(mn),因为需要遍历所有m*n个状态。空间复杂度标准解法是O(mn),但可以优化,比如用一维数组,因为当前状态只依赖上一行和当前列,所以只需要O(min(m,n))的空间。优化方法是交换字符串使较短的那个作为列,遍历时用prev变量保存上一行的值,这样空间复杂度降到O(min(m,n))。比如对于s1='ABCBDAB'和s2='BDCABA',计算后LCS是'BCBA',长度4。”
6) 【追问清单】
7) 【常见坑/雷区】