python-动态规划算法-最大公共字符串和最大公共子序列区别?
一、动态规划的问题解决思路和区别
''' 动态规划: 动态规划,是一种以空间换时间的技术,算法的根本目是解决冗余(重复计算)。 1 每种动态规划解决方案都涉及网格; 2 单元格中的值通常是你要优化的值; 3 每个单元格都是一个子问题,因此你应该考虑如何将问题分成子问题,这有助于你找出网格的坐标轴; 最长公共子串和最长公共子序列,区别? 最长公共子串要求在原字符串中是连续的,而子序列只需要保持相对顺序一致,并不要求连续。 '''
二、执行结果
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
获取最长公共子串
1 x x 1 2 3 4 5 6 x x 1
2 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
获取最长公共子串
1 x x 1 2 3 4 5 6 x x 1
2 0 0 0 0 1 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
1 1 0 0 1 0 0 0 0 0 0 0 1
2 0 0 0 0 2 0 0 0 0 0 0 0
3 0 0 0 0 0 3 0 0 0 0 0 0
4 0 0 0 0 0 0 4 0 0 0 0 0
5 0 0 0 0 0 0 0 5 0 0 0 0
6 0 0 0 0 0 0 0 0 6 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
1 1 0 0 1 0 0 0 0 0 0 0 1
1 1 0 0 1 0 0 0 0 0 0 0 1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
s1(13) = 2yy123456yy11
s2(12) = 1xx123456xx1
最长公共子串:123456 索引:3 长度:6
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
最长公共子序列
1 x x 1 2 3 4 5 6 x x 1
2 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
最长公共子序列
1 x x 1 2 3 4 5 6 x x 1
2 0 0 0 0 1 1 1 1 1 1 1 1
y 0 0 0 0 1 1 1 1 1 1 1 1
y 0 0 0 0 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 2
2 1 1 1 1 2 2 2 2 2 2 2 2
3 1 1 1 1 2 3 3 3 3 3 3 3
4 1 1 1 1 2 3 4 4 4 4 4 4
5 1 1 1 1 2 3 4 5 5 5 5 5
6 1 1 1 1 2 3 4 5 6 6 6 6
y 1 1 1 1 2 3 4 5 6 6 6 6
y 1 1 1 1 2 3 4 5 6 6 6 6
1 1 1 1 2 2 3 4 5 6 6 6 7
1 1 1 1 2 2 3 4 5 6 6 6 7
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
s1(13) = 2yy123456yy11
s2(12) = 1xx123456xx1
最长公共序列:2134561 长度:7 times:156
三、代码
#!/usr/bin/env python3
# coding=utf-8
'''
动态规划:
动态规划,是一种以空间换时间的技术,算法的根本目是解决冗余(重复计算)。
1 每种动态规划解决方案都涉及网格;
2 单元格中的值通常是你要优化的值;
3 每个单元格都是一个子问题,因此你应该考虑如何将问题分成子问题,这有助于你找出网格的坐标轴;
最长公共子串和最长公共子序列,区别?
最长公共子串要求在原字符串中是连续的,而子序列只需要保持相对顺序一致,并不要求连续。
'''
times = 0
def printDp(dp, s1, s2,msg=''):
print('~'*60)
print(msg)
rows = tuple(s1)
cols = tuple(s2)
txt = '\t'.join(cols)
print('\t%s'%txt)
for i in range(len(rows)):
arr = []
arr.append(str(rows[i]))
for j in range(len(cols)):
arr.append(str(dp[i][j]))
print('\t'.join(arr))
print('~'*60)
def getDp(rowLen,colLen):
arr = [[0 for i in range(colLen)] for i in range(rowLen)]
return arr
def commonSubstring(s1,s2):
'''
动态规划,获取最长公共子串:
即获取二维数组中,连续且最长的对角线,对应坐标所上的字符;
动态规划思考的问题:
1 动态规划都涉及网格,name网格的 x、y 轴,分别是什么?-- x、y 分别为 s1、s2 中的字符;
2 单元格中的值存什么?-- 存索引位置相邻的公共字符串长度值;
3 知道公共字符串起始索引和公共字符串长度,就可以在s1或s2中将公共字符串截取出来;
逻辑描述:
1 构建一个长为 len(s1) 宽为 len(s2) 的矩阵,每个坐标点默认值为0;
2 比较 x,y 轴对应的字母是否相同;
3 相同则:当前坐标值 = 左上角坐标值+1;
'''
rowLen = len(s1)
colLen = len(s2)
rows = tuple(s1)
cols = tuple(s2)
dp = getDp(rowLen,colLen) # 构建图
msg = '获取最长公共子串'
printDp(dp,s1,s2,msg)
maxLen = 0 # 最长公共字符串长度
maxIndex = 0 # 最大长度子串终止索引
for i in range(rowLen):
for j in range(colLen):
r = rows[i]
c = cols[j]
# 比较字符是否相同
if r == c:
v = 0
# 左上角单元格值
x = i - 1
y = j - 1
if x >= 0 and y >= 0:
v = dp[x][y]
v += 1
if maxLen < v:
maxLen = v
maxIndex = j # i、j 随便一个即可,因为两个值相同
dp[i][j] = v
printDp(dp,s1,s2,msg)
strIndex = maxIndex - maxLen + 1
commonStr = s1[strIndex:maxIndex+1] # 截取时注意开闭区间问题
print('s1(%s) = %s'%(len(s1),s1))
print('s2(%s) = %s'%(len(s2),s2))
print('最长公共子串:%s\t索引:%s\t长度:%s'%(commonStr,strIndex,maxLen))
return commonStr
def commonSubsequence(s1,s2):
'''
动态规划,获取最长公共子序列(注:子序列不要求字符串连续):
同样是获取二维数组中对角线最长的坐标,但不要求连续,所对应的字符;
动态规划思考的问题:
1 动态规划都涉及网格,name网格的 x、y 轴,分别是什么?-- x、y 分别为 s1、s2 中的字符;
2 单元格中的值存什么?-- 公共序列长度;
逻辑描述:
1 构建一个长为 len(s1) 宽为 len(s2) 的矩阵,每个坐标点默认值为0;
2 比较 x,y 轴对应的字母是否相同;
3 相同则:当前坐标值 = max(左侧值,上方值)+1;
'''
times = 0
rowLen = len(s1)
colLen = len(s2)
rows = tuple(s1)
cols = tuple(s2)
dp = getDp(rowLen,colLen) # 构建图
msg = '最长公共子序列'
printDp(dp,s1,s2,msg)
maxLen = 0 # 最长公共字符串长度
arr = [] # 记录相同字符
for i in range(rowLen):
for j in range(colLen):
times += 1
r = rows[i]
c = cols[j]
if r == c:
# 相同时,当前值 = 相同左上值 + 1
v = 0
# 左上角单元格值
x = i - 1
y = j - 1
if x >= 0 and y >= 0:
v = dp[x][y]
v += 1
if maxLen < v:
maxLen = v
arr.append(rows[i]) #记录公共子序列
dp[i][j] = v
else:
# 不同时,当前值 = max(左侧值,上方值) 取最大
# 上侧值
tv = 0
tx = i
ty = j-1
if tx >= 0 and ty >= 0:
tv = dp[tx][ty]
# 左侧值
lv = 0
lx = i - 1
ly = j
if lx >= 0 and ly >= 0:
lv = dp[lx][ly]
v = max(tv,lv)
dp[i][j] = v
printDp(dp,s1,s2,msg)
commonStr = ''.join(arr)
print('s1(%s) = %s'%(len(s1),s1))
print('s2(%s) = %s'%(len(s2),s2))
print('最长公共序列:%s\t长度:%s\ttimes:%s'%(commonStr,maxLen,times))
pass
def main():
s1 = '2yy123456yy11'
s2 = '1xx123456xx1'
commonSubstring(s1,s2)
print('\n\n')
commonSubsequence(s1,s2)
if __name__ == '__main__':
main()