[python] 获得所有的最长公共子序列

两句闲话

  得到两个序列的最长公共子序列(LCS)是个经典问题,使用动态规划,实现起来并不难。

  一般来说,我们只是输出一个LCS。但是,老师布置的作业是输出所有的LCS。

解法

  按照一般的方法,我们首先得到一个矩阵,然后从矩阵的右下角开始回溯。回溯时,我们选择较大的数字,以向左,或向上,或向左上。但当数字相等时,我们往往会随便向某一个方向回溯,这样的话,我们就只会得到一个LCS。因此,很容易想到,所有的LCS会构成一棵树,我们只需要对这棵树进行先序遍历,就可得到所有的LCS。

  

  代码如下

#python 3.5

class LCS_naive:
	"""
	最长公共子序列:
		通过动态规划,得到矩阵D,
		并从矩阵D中读出一个最长公共子序列
		不支持读出所有的LCS
	"""
	def __init__(self):
		self.matrix=[[]]

	def init(self,str1,str2):
		self.str1=str1
		self.str2=str2
		self.len1=len(str1)
		self.len2=len(str2)
		self.matrix=[[0 for i in range(self.len2+1)]for j in range(self.len1+1)]

	def _get_matrix(self):
		"""通过动态规划,构建矩阵"""
		for i in range(self.len1):
			for j in range(self.len2):
				if self.str1[i]==self.str2[j]:
					self.matrix[i+1][j+1]=self.matrix[i][j]+1
				else:
					self.matrix[i+1][j+1]=max(self.matrix[i][j+1],self.matrix[i+1][j])

	def _matrix_show(self,matrix):
		"""展示通过动态规划所构建的矩阵"""
		print ("----matrix-----")
		print (" "," ",end=" ")
		for ch in self.str2:
			print (ch,end=" ")
		print () 
		for i in range(len(matrix)):
			if i>0: print (self.str1[i-1],end=" ")
			else: print (" ",end=" ")
			for j in range(len(matrix[i])):
				print (matrix[i][j],end=" ")
			print () 
		print ("---------------")

	def _get_one_lcs_from_matrix(self):
		i=len(self.matrix)-1
		if i==0:
			print ("matrix is too small")
			return
		j=len(self.matrix[0])-1
		res=[]
		while not (i==0 or j==0):
			if self.str1[i-1]==self.str2[j-1]:
				res.append(self.str1[i-1])
				i-=1
				j-=1
			else:
				if self.matrix[i-1][j]>self.matrix[i][j-1]:
					i=i-1
				else:
					j=j-1
		return "".join(res[::-1])

	def get_lcs(self):
		self._get_matrix()
		self._matrix_show(self.matrix)
		lcs=self._get_one_lcs_from_matrix()
		print (lcs)

class LCS(LCS_naive):
	"""
	继承自LCS_naive
	增加获取所有LCS的支持
	"""
	def __init__(self):
		LCS_naive.__init__(self)

	def _get_all_lcs_from_matrix(self):
		self._pre_travesal(self.len1,self.len2,[])

	def _pre_travesal(self,i,j,lcs_ted):
		if i==0 or j==0:
			print ("".join(lcs_ted[::-1]))
			return 
		if self.str1[i-1]==self.str2[j-1]:
			lcs_ted.append(self.str1[i-1])
			self._pre_travesal(i-1,j-1,lcs_ted)
		else:
			if self.matrix[i-1][j]>self.matrix[i][j-1]:
				self._pre_travesal(i-1,j,lcs_ted)
			elif self.matrix[i-1][j]<self.matrix[i][j-1]:
				self._pre_travesal(i,j-1,lcs_ted)
			else:
				###### 分支
				self._pre_travesal(i-1,j,lcs_ted[:])
				self._pre_travesal(i,j-1,lcs_ted)

	def get_lcs(self):
		self._get_matrix()
		self._matrix_show(self.matrix)
		self._get_all_lcs_from_matrix()
		

l=LCS()
l.init("ABCBDAB","BDCABA")
l.get_lcs()

输出结果

----matrix-----
    B D C A B A 
  0 0 0 0 0 0 0 
A 0 0 0 0 1 1 1 
B 0 1 1 1 1 2 2 
C 0 1 1 2 2 2 2 
B 0 1 1 2 2 3 3 
D 0 1 2 2 2 3 3 
A 0 1 2 2 3 3 4 
B 0 1 2 2 3 4 4 
---------------
BCBA
BCAB
BDAB

  

 

转载于:https://www.cnblogs.com/super-zhang-828/p/6486452.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值