【每日一练】最长斐波那契子序列

▼魔方 西西 提交于 2019-12-02 08:42:55

题目描述

给定一个数组,找出其中最长的子序列,满足该子序列是斐波那契子序列

注:
如果序列 X1,X2,...,XnX_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:

  • n >= 3
  • 对于所有 i + 2 <= n,都有 Xi+Xi+1=Xi+2X_i + X_{i+1} = X_{i+2}

思路:

本题一开始想岔了,一直以为Fibonacci数列必须是严格按照{1,1,2,3,5,8……}的顺序的数组,后来才知道只要满足上式均可。

方法一:暴力算法,由于F数列每个数的生成只依赖前两个数,假设以x = a[i],y = a[j]为序列起始,只需计算下一个数字P = x + y,并判断P是否存在后续数组中。每次更新x,y \leftarrow y,P。

class Solution(object):
    def lenLongestFibSubseq(self, A):
        ans = 0
        n = len(A)
        for i in range(n):
            for j in range(i+1, n):
                x, y = A[j], A[i] + A[j]
                length = 2
                while y in A[j+1: n]:
                    x, y = y, x + y
                    length += 1
                ans = max(ans, length)
        return ans if ans >= 3 else 0

方法一有三重嵌套循环,所以时间复杂度为O(n2logm)O(n^2log^m),其中m是序列A中最大值。对于严格递增序列,可以使用set保存序列中元素,快速判断目标值P是否存在于数组中,是时间复杂度降到O(n2)O(n^2),严格递增的条件限制可以保证“如果目标值P存于在数组中,那么这个目标值一定在起始值x,y的后面”,

方法二:动态规划。我们将(a[i],a[j]), (a[j], a[k])都看做结点,假如满足a[k] = a[i] + a[j],那么我们认为两个结点之间是连通的。那么对于一个F序列[1,2,3,5,8,13],相当于存在一条完整路径(1,2)→(2,3)→(3,5)→(5,8)→(8,13)。

假设dp[i][j]是以(a[i], a[j])为最后结点的路径长度,那么当存在数值a[k] = a[i] + a[j]时,(a[j],a[k])可以加入这条路径,并且路径长度加1,即满足:
dp[j][k]=dp[i][j]+1dp[j][k] = dp[i][j] + 1 if a[k]=a[i]+a[j]a[k] = a[i] + a[j]k>jk > j
dp[j][k]=2dp[j][k] = 2 otherwise

class Solution(object):
    def lenLongestFibSubseq(self, A):
    	# 建立倒排字典
        index = {x: i for i, x in enumerate(A)}
        longest = collections.defaultdict(lambda: 2)

        ans = 0
        for k, z in enumerate(A):
            for j in range(k):
                i = index.get(z - A[j], None)
                if i is not None and i < j:
                    cand = longest[j, k] = longest[i, j] + 1
                    ans = max(ans, cand)

        return ans if ans >= 3 else 0

方法三:对于非严格递增序列,存在重复数值,例如[0,1,1,2],不能简单的使用dict保存下标,因此采用collections中的默认字典来保存每个数值出现的所有位置。

def lenLongestFibSubseq(A):
    import collections
    n = len(A)
    index = collections.defaultdict(list)
    for i in range(n):
        index[A[i]].append(i)
    longest = collections.defaultdict(lambda: 2)

    ans = 0
    for k, z in enumerate(A):
        for j in range(k):
            # 找到前面所有满足条件的数字下标
            i_index = index.get(z - A[j], [])
            if i_index:
                m = len(i_index)
                # 最近原则,取离j最近的下标
                for i in range(m-1, -1, -1):
                    if i_index[i] < j:
                        cand = longest[j, k] = longest[i_index[i], j] + 1
                        ans = max(ans, cand)
                        break
    return ans if ans >= 3 else 0

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!