LCS、LIS、LCString:算法面试中常见解决思路和代码

LCS、LIS、LCString:算法面试中常见解决思路和代码

1. LCS:largest common subsequence最长公共子序列

假定两个字符串a、b长度分别为m和n,且有m<=n.

1.1 暴力求解BF

将两个字符串中较短的一个进行全排列,一共的2^m个可能的序列,然后分别与另一个字符串进行比对,找出最长的子序列。

1.2 动态规划

如果ai == bj,那么cij等于c[i-1][j-1]+1;

如果ai != bj,那么有两种情况要么是ai-1与bj的组合的结果;要么是ai与bj-1的自核结果。如下图:

即可求出cij这个二维数组,里面的每一个元素代表了a、b字符串矩阵中的最大子序列的长度:

 1 def lcs(s1, s2):
 2     res = [([0] * (len(s2) + 1)) for i in range(len(s1) + 1)]
 3     flag = [([0] * (len(s2) + 1)) for i in range(len(s1) + 1)]
 4     for i in xrange(len(s1) + 1):
 5         for j in xrange(len(s2) + 1):
 6             if i == 0 or j == 0:
 7                 res[i][j] = 0
 8 
 9             elif s1[i - 1] == s2[j - 1]:
10                 res[i][j] = res[i - 1][j - 1] + 1
11                 flag[i][j] = 'o'
12             else:
13                 if res[i - 1][j] < res[i][j - 1]:
14                     res[i][j] = res[i - 1][j]
15                     flag[i][j] = 'l'
16                 else:
17                     res[i][j] = res[i][j - 1]
18                     flag[i][j] = 'u'
19 
20     #return res[len(s1)][len(s2)]
21     return res, flag

该代码我是考虑的之后子序列回溯的问题——flag,无伤大雅。

如果仅仅是想要得到最长的子序列长度的话,只需要得到res[len(s1)][len(s2)]即可。

该两图摘自网络博客,具体链接见底部。

 

回溯找到LCS代码如下:

 

 1 def lcs2(s1, s2):
 2     flag = lcs(s1, s2)
 3     res = ''
 4     i = len(flag)-1
 5     j = len(flag[0])-1
 6     while i != 0 and j != 0:
 7         print i,j
 8         if flag[i][j] == 'o':
 9             res += s1[i-1]
10             i -= 1
11             j -= 1
12         elif flag[i][j] == 'l':
13             j -= 1
14         else:
15             i -= 1
16     return ''.join(reversed(res))

 

2. LCString最长公共子串问题

与LCS的区别在于是否连续

其实已经有了LCS的思想,再求公共子串的更加容易,只需将动态规划的思想改变一下,当遇到ai != bj的情况令cij=0即可。

细节不再赘述。

 1 def lcstring(s1, s2):
 2     res = [([0] * (len(s2) + 1)) for i in range(len(s1) + 1)]
 3     maxL = 0
 4     for i in xrange(len(s1) + 1):
 5         for j in xrange(len(s2) + 1):
 6             if i == 0 or j == 0:
 7                 res[i][j] = 0
 8 
 9             elif s1[i - 1] == s2[j - 1]:
10                 res[i][j] = res[i - 1][j - 1] + 1
11                 maxL = max(res[i][j], maxL)
12             else:
13                 res[i][j] = 0
14     return maxL

回溯代码没有必要,有很多种方式

3. LIS:largest increament subsequence 最长递增子序列

最长递增子序列并不是两个数列(其实字符串和数列都无所谓)

3.1 捷径算法

首先说一下一种捷径,如果已经实现了LCS,那么只需要一步即可得到LIS:

将该数列进行排序,然后与原数列进行LCS即可得到LIS!!!!

原数列:4,7,2,5,8,9    =a

排序后:2,5,4,7,8,9    =b

对其调用lcs(a, b)即可。

3.2 暴力求解BF

比如数列 4,7,2,5,8,9

第一个数为4,直接明了:res[1]

第二个数为7,有比他小的所以:res[2] = res[1] + 1

第三个数为2,前面没有比他小的res[3] = 1

第四个数为5,只有一个比他小的res[4] = res[3] + 1

第五个数为8,前面有好几个比他小的,所以找到max(res...) + 1

.......

一句话:就是找到在i之前的比i小的最大长度的序列的长度,很明显是n^2的复杂度。

 1 def lis(l):
 2     res = [0] * len(l)
 3     maxl = 0
 4     for i in xrange(len(l)):
 5         res[i] = 1
 6         for j in xrange(i):
 7             if l[i] >= l[j] and (res[j]+1) >= res[i]:
 8                 res[i] = res[j] + 1
 9         if maxl < res[i]:
10             maxl = res[i]
11     return maxl

回溯的话直接遍历res找到每一个元素即可。

3.3 贪心+二分法

自行维护一个数组res[] 用来记录最小序列的可能性,但是并不是真正的最长递增序列的真是值。目的仅仅是将其LIS的长度计算出来!!!!如果想要得到真正的LIS序列,需要记录其他的东西。

举个栗子:4,7,2,5,8,9

step1:res[4]

step2:res[4,7]

step3:res[2,7]

step4:res[5,7]

step5:res[5,7,8]

step:res[5,7,8,9]

递增的长肯定是res的长度,但是真是的LIS并不是5789而是2589或者4589或者4789并不是唯一的。

二分的作用就是在更换res的时候的时间复杂度从n^2变成nlogn

 

 1 def binary_search(l, a):
 2     end = len(l)
 3     start = 1
 4     while end > start:
 5         mid = (end + 1) >> 1
 6         if l[mid] <= a:
 7             start = mid + 1
 8         else:
 9             end = mid - 1
10     return start
11 
12 def lis2(l):
13     res = []
14     for i in xrange(len(l)):
15         if len(res) == 0 or res[-1] < l[i]:
16             res.append(l[i])
17         elif res[-1] > l[i]:
18             index = binary_search(res, l[i])
19             res[index-1] = l[i]
20     return len(res)

3.4 树状数组维护

https://blog.csdn.net/George__Yu/article/details/75896330

参考链接:

https://blog.csdn.net/qq_31881469/article/details/77892324

https://blog.csdn.net/lxt_Lucia/article/details/81206439

http://www.cnblogs.com/GodA/p/5180560.html

 

有任何疑问请留言或者发送至邮箱397585361@qq.com

posted @ 2018-11-21 16:59  亮亮·Leo  阅读(427)  评论(0编辑  收藏  举报