【Leetcode】873-1911

前言

做完之后看了一下之前的提交记录,感觉之前就是碰运气而已,竟然感觉最应该做出来的DP没有,仅仅用到了枚举+hash


思路

如果已经给定一个斐波那契数列,那么如何判断后续的字符其实就十分确定了,就是判断某些数字是否存在,而又因为题目给定了数组是排好序的,因此,如果我们枚举斐波那契数列的起始两个位置,之后判断后续每个位置之后不断更新即可。据此可以写出暴力破解法的代码

class Solution:
    def lenLongestFibSubseq(self, arr: List[int]) -> int:
        n = len(arr)
        ans = 0
        # 1. 暴力破解
        for i in range(n):
            for j in range(i+1,n):
                cur = 2
                a,b = i,j 
                for k in range(j+1,n):
                    if arr[k]==arr[a]+arr[b]:
                        a,b = b,k
                        cur += 1
                ans = max(ans, cur)
        return ans if ans>=3 else 0 

可以看到的是,上述代码的时间复杂度为 \(O(n^3)\),但是经过最初的分析我们可以得到的结论是:如果给定初始斐波那契的两个位置,那么其实后续所有斐波那契数列的数字都是固定的,此时只需要逐个判断这些数字是否存在于原数组即可。

因此可以提前将数组中的数字存储于hash表中,此后枚举初始的两个位置,之后直接判断后续斐波那契数字是否在hash中即可。

class Solution:
    def lenLongestFibSubseq(self, arr: List[int]) -> int:
        t = dict()
        for i,x in enumerate(arr):
            t[x] = i

        ans = 0
        n = len(arr)
        for i in range(n):
            for j in range(i+1,n):
                a,b = arr[i],arr[j]
                length = 2
                while a+b in t:
                    a,b = b,a+b
                    length+=1
                ans = max(ans,length)
        return ans if ans >= 3 else 0

以上代码的时间复杂度分析,两层枚举为\(n^2\),而枚举后续的看似也是一个\(n\)的时间复杂度,但是实际上由于斐波那契数列的增加不是线性的,而增大到可能存在的最大数字\(10^9\),枚举的次数不超过50次,可以视为常数。因此该算法的时间复杂度为\(O(Cn^2)\),C为常数

此外,从另外一个角度出发,我们考虑 使用DP,其中\(dp[i][j]\)表示以\(arr[i]\)\(arr[j]\)为斐波那契数列的最后两个数字时候斐波那契数列的长度。
\(dp[i][j]=dp[j][had[arr[i]-arr[j]]]\)

需要注意的是,因此给定的数组一定是严格递增的,因此需要满足\(arr[i]-arr[j] \neq arr[j]\)。此外,有因此如果找到了\(arr[i]-arr[j]\),那么此时最短的斐波那契数列是3,因此需要将公式更新为:
\(dp[i][j]=max(dp[j][had[arr[i]-arr[j]]], 3)\)

class Solution:
    def lenLongestFibSubseq(self, arr: List[int]) -> int:
        n = len(arr)
        ans = 0
        # dp[i][j]表示以 arr[i] 和 arr[j] 为最后两个元素的斐波那契子序列的长度
        # dp[i][j] = dp[j][had[arr[i]-arr[j]]]
        # had[x] 表示 x在arr中的位置
        dp = [[0] * n for _ in range(n)]
        had = dict()
        ans = 0
        for i in range(n):
            for j in range(i):
                if arr[j]*2<=arr[i] or arr[i] - arr[j] not in had:continue
                dp[i][j] = max(dp[j][had[arr[i] - arr[j]]]+1, 3)
                ans = max(ans, dp[i][j])
            had[arr[i]] = i
        return ans
posted @ 2024-07-26 22:30  TICSMC  阅读(4)  评论(0)    收藏  举报