Leetcode-公共子序列

Leetcode-公共子序列

在这里插入图片描述

基本思路

连续与否
最长子序列(LCS)不要求连续性
字串/字数组要求连续性

启发思路:本质是选/不选,考虑test1和test2的最后字母x,y,有四种情况:

s1s2s3s4
不选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(i1,j),dfs(i,j1),dfs(i1,j1)+1) if s[i]==t[j]dfs(i,j)=max(dfs(i1,j),dfs(i,j1),dfs(i1,j1)) 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(i1,j),dfs(i,j1)>dfs(i1,j1)+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(i1,j1)>dfs(i1,j) or dfs(i1,j1)>dfs(i,j1)?同样不必,因为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(i1,j1)+1   s(i)=t(j)dfs(i,j)=max(dfs(i1,j),dfs(i,j1))   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]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值