Python程序员面试算法宝典---解题总结: 第5章 字符串 5.15 如何求最长递增子序列的长度

# -*- coding: utf-8 -*-

'''
Python程序员面试算法宝典---解题总结: 第5章 字符串 5.15 如何求最长递增子序列的长度

题目:
假设L=<a1, a2, ...,an>是n个不同的实数的序列,L的递增子序列是这样的一个子序列
Lin=<ak1, ak2,...,akm>,其中k1<k2<...<km且ak1<ak2<...<akm。
求最大的m值。

分析:
举例分析,假设数组为
[3, 1, 3, 8, 5, 6, 9]
实际就是选取
[3, 5, 6, 9]
还是选取
[1, 3, 5, 6, 9]的区别
典型的动态规划题目。
假设f[i]表示L[i]结尾的最长递增子序列的长度
那么有递推公式如下:
f[i+1] = { f[i] + 1, if L[i+1] > L[i]
        { f[i], if L[i+1] <= L[i]
但这样这个公式就错了,它会导致只要后一个元素只要大于前一个元素就被选中,
导致带来刚才分析的问题,即最终选取的是
[3, 5, 6, 9]
而不是
[1, 3, 5, 6, 9]
因为一个元素要么选取,要么不选取。

关键就是需要遍历所有<i中的所有下标,选择最长递增子序列
第i个元素的最长递增子序列长度是和其前面的元素相关
f[i] = {1, max f[j] + 1} , j < i and L[j] < L[i]
f[0] = 1

关键:
1 之所以没有想到
忘记动态规划中:
当前元素的状态是通过之前的元素或元素列表可以得到,
另外需要遍历当前元素之前的每个元素来确定究竟选择
当前元素前面的哪个元素来构成最长递增子序列
f[i] = max{1, max f[j] + 1}, j < i and L[j] < L[i]

参考:
Python程序员面试算法宝典
'''

def lis(array):
    if not array:
        return
    size = len(array)
    dp = [0 for i in range(size)]
    # 记录结果的数组
    result = [0 for i in range(size)]
    dp[0] = 1
    maxLen = 0
    maxIndex = -1
    maxResultIndex =0
    # 递推
    for i in range(1, size):
        for j in range(0, i):
            if array[j] < array[i]:
                maxValue = max(1, dp[j] + 1)
                if maxValue > dp[i]:
                    dp[i] = maxValue
                    maxIndex = j
            else:
                dp[i] = max(1, dp[i])
        # 说明当前是存在递增子序列的
        if maxIndex != -1:
            result[maxIndex] = 1
        if dp[i] > maxLen:
            maxLen = dp[i]
            maxResultIndex = i
    # 还缺少最后一个元素.就是最长递增子序列长度最长的下标
    result[maxResultIndex] = 1
    return maxLen, result


def process():
    array = [3, 1, 3, 8, 5, 6, 9]
    maxLen, result = lis(array)
    print maxLen
    info = ""
    for index, value in enumerate(result):
        if 1 == value:
            if info:
                info += " " + str(array[index])
            else:
                info += str(array[index])
    print info


if __name__ == "__main__":
    process()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值