Leetcode-公共子序列

基本思路
| 子 | 连续与否 |
|---|---|
| 最长子序列(LCS) | 不要求连续性 |
| 字串/字数组 | 要求连续性 |
启发思路:本质是选/不选,考虑test1和test2的最后字母x,y,有四种情况:
| s1 | s2 | s3 | s4 |
|---|---|---|---|
| 不选x不选y | 不选x选y | 选x不选y | 选x选y |
暴力解法-回溯硬搜
回溯三问:
| 步骤 | 描述 | 公式 |
|---|---|---|
| 当前操作 | s[i],t[j]选,还是不选 | ~ |
| 子问题(等式左边) | s的前i个字母和t的前j个字母的最长子序列长度 | ~ |
| 子子问题(等式右边) | s的前(i-1)个字母和t的前(j-1)个字母的LCS长度 | dfs(i-1,j-1)+s[i]==t[j] |
| ~ | s的前(i-1)个字母和t的前j个字母的LCS长度 | dfs(i-1,j) |
| ~ | s的前i个字母和t的前(j-1)个字母的LCS长度 | dfs(i,j-1) |
递归公式为:
dfs(i,j)=max(dfs(i−1,j),dfs(i,j−1),dfs(i−1,j−1)+1) if s[i]==t[j]dfs(i,j)=max(dfs(i−1,j),dfs(i,j−1),dfs(i−1,j−1)) if s[i]!=t[j]
dfs(i,j) = max(dfs(i-1,j),dfs(i,j-1),dfs(i-1,j-1)+1)\ if \ s[i]==t[j] \\
dfs(i,j) = max(dfs(i-1,j),dfs(i,j-1),dfs(i-1,j-1)) \ if \ s[i]!=t[j]
dfs(i,j)=max(dfs(i−1,j),dfs(i,j−1),dfs(i−1,j−1)+1) if s[i]==t[j]dfs(i,j)=max(dfs(i−1,j),dfs(i,j−1),dfs(i−1,j−1)) if s[i]!=t[j]
注意:都选和都不选的子问题是相同的,都会递到i-1和j-1
思考1:当s[i]==t[j]时需要考虑保留其中一个的可能么,即当二者相同时,还需要考虑s(i-1)和t(j)之间的最小子序列长度么,它的长度会比二者相同时,都加入子序列的这种情况更长么
∃dfs(i−1,j),dfs(i,j−1)>dfs(i−1,j−1)+1?
\exists dfs(i-1,j),dfs(i,j-1) > dfs(i-1,j-1)+1?
∃dfs(i−1,j),dfs(i,j−1)>dfs(i−1,j−1)+1?
图中s[i] == t[j], 则有x=dfs(i-1,j-1),设dfs(i-1,j)>x+1,(也意味着有考虑的意义)
此时考虑若去掉c,则abd和ab的子序列为>x,但二者都是s和t的子序列,其子序列只能<=x,矛盾,所以dfs(i-1,j)<=dfs(i-1,j-1)
思考2:当s[i]!=t[j]时需要考虑保留两个的可能么:
∃dfs(i−1,j−1)>dfs(i−1,j) or dfs(i−1,j−1)>dfs(i,j−1)?
\exists dfs(i-1,j-1) > dfs(i-1,j)\ or \ dfs(i-1,j-1) > dfs(i,j-1) ?
∃dfs(i−1,j−1)>dfs(i−1,j) or dfs(i−1,j−1)>dfs(i,j−1)?同样不必,因为dfs(i-1,j)和dfs(i,j-1)>=dfs(i-1,j-1)。
综上,递归公式为:
dfs(i,j)=dfs(i−1,j−1)+1 s(i)=t(j)dfs(i,j)=max(dfs(i−1,j),dfs(i,j−1)) s(i)!=t(j)
dfs(i,j) = dfs(i-1,j-1)+1 \ \ \ s(i) = t(j) \\
dfs(i,j) = max(dfs(i-1,j),dfs(i,j-1)) \ \ \ s(i)!= t(j)
dfs(i,j)=dfs(i−1,j−1)+1 s(i)=t(j)dfs(i,j)=max(dfs(i−1,j),dfs(i,j−1)) s(i)!=t(j)
边界条件:
当s or t 便利至空,表示非空字符串和空字符串的公共子序列长度,即:
if i<0 or j<0:
return 0
动态规划
回溯→递推
叶子节点→初始化
def func_base(text1,text2):
m = len(text1)
n = len(text2)
f = [[0] * (n+1) for _ in range(m+1)]
for i in range(m):
for j in range(n):
if text1[i] == text2[j]:
f[i+1,j+1] = f[i,j]+1
else:
f[i+1,j+1] = max(f[i+1,j],f[i,j+1])
return f[m][n]
空间优化1:
使用两个数组,
(两层循环:text1枚举一个x,便利text2)
def func_base(text1,text2):
m = len(text1)
n = len(text2)
#f = [[0] * (n+1) for _ in range(m+1)]
f_even = [0]*(n+1)
f_odd = [0]*(n+1)
for i in range(m):
if i%2==0: # even
for j in range(n):
if text1[i] == text2[j]:
f_even[j+1] = f_odd[j]+1
else:
f_even[j+1] = max(f_even[j],f_odd[j+1])
elif i%2 ==1:
for j in range(n):
if text1[i] == text2[j]:
f_odd[j+1] = f_even[j]+1
else:
f_odd[j+1] = max(f_odd[j],f_even[j+1])
return f_even[n] if m%2 == 1 else f_odd[n]
空间优化2
修改为一个数组
f[i+1,j+1]涉及到的元素:
| 数组元素 | description |
|---|---|
| f[i+1,j] | [m+1,n+1]阵列的左边,使用一维数组为f[j] |
| f[i,j+1 | [m+1,n+1]阵列的上一行,使用一维数组为f[j+1] |
| f[i,j] | [m+1,n+1]阵列的左上,使用一维数组会被阵列的左边覆盖,所以需要单独保存 |
def func_base(text1,text2):
m = len(text1)
n = len(text2)
f = [[0] * (n+1)]
for i in range(m):
pre = 0
for j in range(n):
tmp = f[j+1]
if text1[i] == text2[j]:
f[j+1] = pre+1
else:
f[j+1] = max(f[j],f[j+1])
pre = tmp
return f[n]
714

被折叠的 条评论
为什么被折叠?



