最长递增子序列(LIS)+最长公共子序列(LCS)+最长公共递增子序列(LICS)(非常详细的python示例代码)

一、最长递增子序列(LIS)问题

最长递增子序列(LIS)问题:已知一个序列,找出最长单调递增子序列(不要求连续)
网上只找到计算长度的代码,没有找到输出最长序列的代码,因此只有部分参考价值,此处给出的代码,可以随机输出最长递增子序列中的一个
'''
最长递增子序列(LIS)问题:已知一个序列,找出最长单调递增子序列(不要求连续)
网上只找到计算长度的代码,没有输出最长序列的代码,因此只有部分参考价值
'''
import random
import numpy as np

# 方法一:最长公共子序列法
# a = [7,3,8,6,19,13,7,10]
# 排序并去重
# n = len(a)
# b = np.zeros(n)
# for k in range(n):
#     b[k] = a[k]
# b.sort()
# 查找序列 a 和 b 的最长公共子序列


# 方法二:动态规划法
def LIS(a):
    n = len(a)
    arr_dict = []
    dict = {0: -1}
    arr_dict.append({0: a[0]})
    for i in range(1, n):
        flag = False
        for j in range(len(arr_dict) - 1, -1, -1):
            temp_dict = arr_dict[j]
            for key, value in temp_dict.items():
                if value < a[i]:
                    dict[i] = key
                    if (j == (len(arr_dict) - 1)):
                        arr_dict.append({i: a[i]})
                        flag = True
                        break
                    else:
                        flag = True
                        arr_dict[j+1][i] = a[i]
                        break
            if flag:
                break
        if (not flag):
            arr_dict[0][i] = a[i]
            dict[i] = -1

    # arr_arr 的长度就是最长递增子序列的长度
    length = len(arr_dict)
    print(arr_dict)
    print(dict)

    # 随便取一个最长递增子序列
    sub = np.zeros(length)
    j = length - 1
    last = random.choice(list(arr_dict[j]))
    sub[j] = a[last]
    while j > 0:
        j = j - 1
        last = dict[last]
        sub[j] = a[last]

    return length, sub

#  输入部分
a = [7,26,20,17,8,3,11,22] # 预期输出:7,8,11,12
# a = [7,3,8,6,19,13,7,10] # 预期输出:3,6,7,10
# a = [8,2,1,3,2,4,5,1,2] #预期输出:2,3,4,5
# a = [2,8,3,4,5,1,2]  #预期输出:2,3,4,5

length, sub = LIS(a)
print("最长递增子序列的长度为", length)
print("最长递增子序列是", sub)

二、最长公共子序列(LCS)问题

最长公共子序列(LCS):给定两个序列 X 和 Y,找出最长的公共子序列(不要求连续)
思路参考了:https://blog.csdn.net/ggdhs/article/details/90713154
1、穷举法:pass
2、动态规划法,此代码可以随机输出最长递增子序列中的一个;如果想要输出所有的最长递增子序列,在第三部分(LICS)中有提供这个方法,往后看就行
'''
最长公共子序列(LCS):给定两个序列 X 和 Y,找出最长的公共子序列(不要求连续)
思路参考了:https://blog.csdn.net/ggdhs/article/details/90713154
1、穷举法:pass
2、动态规划法:先输出一个中间数组,res[i][j]表示当str1长度取前i,str2长度取前j时,它们的最长公共子序列的元素个数;然后通过倒推法,往前推荐,找到一个最长公共子序列
'''
import numpy as np

def LCS(string1,string2):
    len1 = len(string1)
    len2 = len(string2)
    res = [[0 for i in range(len1+1)] for j in range(len2+1)]
    for i in range(1,len2+1):
        for j in range(1,len1+1):
            if string2[i-1] == string1[j-1]:
                res[i][j] = res[i-1][j-1]+1
            else:
                res[i][j] = max(res[i-1][j],res[i][j-1])

    # 求出最长公共子序列
    length = res[-1][-1]
    sub = ['' for i in range(length)]
    i = len1  # 8
    j = len2  # 9
    while((i != 0) & (j != 0)):
        if string1[i-1] == string2[j-1]:
            sub[length - 1] = string1[i - 1]
            length = length - 1
            i = i - 1
            j = j - 1
        else:
            try:
                if res[j - 1][i] >= res[j][i - 1]:
                    j = j - 1
                else:
                    i = i - 1
            except:
                a = 1

    for i in range(1,len2+1):
        for j in range(1,len1+1):
            print(res[i][j], " ", end='')
        print()

    return sub,res[-1][-1]

# sub, length = LCS("phenomenon","enughton")
# 预期输出:['e', 'n', 'o', 'n'], 长度为 4
# sub, length = LCS("helloworld","loop")
# 预期输出:['l', 'o', 'o'], 长度为 3
sub, length = LCS("13456778","357486782")
# 预期输出:[3,5,7,7,8], 长度为 5     [3,4,6,7,8] 也对
# res:
# 0  1  1  1  1  1  1  1
# 0  1  1  2  2  2  2  2
# 0  1  1  2  2  3  3  3
# 0  1  2  2  2  3  3  3
# 0  1  2  2  2  3  3  4
# 0  1  2  2  3  3  3  4
# 0  1  2  2  3  4  4  4
# 0  1  2  2  3  4  4  5
# 0  1  2  2  3  4  4  5
print(sub, length)

三、最长公共递增子序列(LICS)

最长公共递增子序列(LICS):结合最长公共子序列问题(LCS)、最长递增子序列问题(LIS)
思路:找出所有的最长公共子序列,分别取它们的最长递增子序列,取最长的作为结果
难点:找出所有的最长公共子序列,参考了:https://blog.csdn.net/qq_33655674/article/details/84062330 
'''
最长公共递增子序列(LICS):结合最长公共子序列问题(LCS)、最长递增子序列问题(LIS)
思路:找出所有的最长公共子序列,分别取它们的最长递增子序列,取最长的作为结果
找出所有的最长公共子序列,参考了:https://blog.csdn.net/qq_33655674/article/details/84062330
'''
import random
import numpy as np

# 构建初始数组,求所有的最长公共子序列
def all_LCS(str1,str2):
    len1 = len(str1)
    len2 = len(str2)

    # len2 是行数, len1 是列数
    res = [[[0, ''] for i in range(len1 + 1)] for j in range(len2 + 1)]

    # i是行,j是列
    for i in range(1, len2 + 1):
        for j in range(1, len1 + 1):
            if (str2[i-1] == str1[j-1]):
                res[i][j][0] = res[i-1][j-1][0] + 1
                res[i][j][1] = '↖'
            else:
                if res[i-1][j][0] > res[i][j-1][0]:
                    res[i][j][0]  = res[i-1][j][0]
                    res[i][j][1] = '↑'
                elif res[i-1][j][0] < res[i][j-1][0]:
                    res[i][j][0] = res[i][j-1][0]
                    res[i][j][1] = '←'
                else:
                    res[i][j][0] = res[i][j - 1][0]
                    res[i][j][1] = '┘'

    # 将得到的数组输出
    # for i in range(1, len2 + 1):
    #     for j in range(1, len1 + 1):
    #         if j != len1:
    #             print(res[i][j], end=" ")
    #         else:
    #             print(res[i][j])

    sub_set = set()
    backTrace(res, str1, len2, len1, '', res[-1][-1][0], sub_set)
    return sub_set

# 递归调用,求所有的最长公共子序列
def backTrace(res, str1, i, j, sub, longest_len, sub_set):
    if ((i == 0) & (j == 0)):
        sub = sub[::-1]
        if len(sub) == longest_len:
            sub_set.add(sub)
        return

    if res[i][j][1] == '↖':
        sub = sub + str1[j - 1]
        backTrace(res, str1, i-1, j-1, sub, longest_len, sub_set)
    elif res[i][j][1] == '←':
        backTrace(res, str1, i, j-1, sub, longest_len, sub_set)
    elif res[i][j][1] == '↑':
        backTrace(res, str1, i-1, j, sub, longest_len, sub_set)
    elif res[i][j][1] == '┘':
        backTrace(res, str1, i, j - 1, sub, longest_len, sub_set)
        backTrace(res, str1, i-1, j, sub, longest_len, sub_set)
    else:
        backTrace(res, str1, 0, 0, sub, longest_len, sub_set)

# 求最长递增子序列
def LIS(a):
    n = len(a)
    arr_dict = []
    dict = {0: -1}
    arr_dict.append({0: a[0]})
    for i in range(1, n):
        flag = False
        for j in range(len(arr_dict) - 1, -1, -1):
            temp_dict = arr_dict[j]
            for key, value in temp_dict.items():
                if value < a[i]:
                    dict[i] = key
                    if (j == (len(arr_dict) - 1)):
                        arr_dict.append({i: a[i]})
                        flag = True
                        break
                    else:
                        flag = True
                        arr_dict[j+1][i] = a[i]
                        break
            if flag:
                break
        if (not flag):
            arr_dict[0][i] = a[i]
            dict[i] = -1

    # arr_arr 的长度就是最长递增子序列的长度
    length = len(arr_dict)

    # 随便取一个最长递增子序列
    sub = ['' for i in range(length)]
    j = length - 1
    last = random.choice(list(arr_dict[j]))
    sub[j] = a[last]
    while j > 0:
        j = j - 1
        last = dict[last]
        sub[j] = a[last]

    sub_str = "".join(sub)

    return length, sub_str

# 输入
sub_set = all_LCS("13456778","357486782") # 预期输出:最长公共递增子序列有但不限于 {'35678', '34678'} , 长度为 5
# sub_set = all_LCS("821324512","2834512") # 预期输出:最长公共递增子序列有但不限于 {'2345'} , 长度为 4
# sub_set = all_LCS("XBACBDEAB","BXCDEAB")  # 预期输出:最长公共递增子序列有但不限于 {'BCDE'} , 长度为 4
# sub_set = all_LCS("abcicba","abdkscab")  # 预期输出:最长公共递增子序列有但不限于 {'abc'} , 长度为 3
# sub_set = all_LCS("ABCBDAB","BDCABA")  # 预期输出:最长公共递增子序列有但不限于 {'BC', 'BD'} , 长度为 2

print("所有的最长公共子序列是", sub_set)
max_len = 0
max_sub_set = set()
for str in sub_set:
    length, sub_str = LIS(str)
    if length > max_len:
        max_len = length
        max_sub_set.clear()
        max_sub_set.add(sub_str)
    elif length == max_len:
        max_sub_set.add(sub_str)

print("最长公共递增子序列有但不限于", max_sub_set, ", 长度为", max_len)

以上代码全部都是自测,没有在专门的算法网站中测试过,所以如果有什么问题,请大家积极留言,这样才能不断完善,谢谢大家!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我有明珠一颗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值