题目描述:
给定一个数组,找出其中最长的子序列,满足该子序列是斐波那契子序列
注:
如果序列 X1,X2,...,Xn 满足下列条件,就说它是 斐波那契式 的:
- n >= 3
- 对于所有 i + 2 <= n,都有 Xi+Xi+1=Xi+2
思路:
本题一开始想岔了,一直以为Fibonacci数列必须是严格按照{1,1,2,3,5,8……}的顺序的数组,后来才知道只要满足上式均可。
方法一:暴力算法,由于F数列每个数的生成只依赖前两个数,假设以x = a[i],y = a[j]为序列起始,只需计算下一个数字P = x + y,并判断P是否存在后续数组中。每次更新x,y ← 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),其中m是序列A中最大值。对于严格递增序列,可以使用set保存序列中元素,快速判断目标值P是否存在于数组中,是时间复杂度降到O(n2),严格递增的条件限制可以保证“如果目标值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]+1 if a[k]=a[i]+a[j]且 k>j
dp[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