Leetcode 873. 最长的斐波那契子序列的长度
一道普通的dp。
首先,我们很容易想到,设$d[i][j]$是以$arr[i]$和$arr[j]$为结尾两个数的最长长度。那么状态转移就是$d[i][j]=max{d[k][i]}+1;(arr[k]+arr[i]==arr[j],k<i<j)$。那么我们可以写出类似下面这样$O(n^3)$的代码:
int d[1007][1007]; class Solution { public: int lenLongestFibSubseq(vector<int>& arr) { int lim=arr.size(); int ans=0; for (int i=0;i<lim;i++) for (int j=0;j<lim;j++) d[i][j]=0; for (int j=2;j<lim;j++) for (int i=1;i<j;i++) for (int k=0;k<i;k++) if (arr[k]+arr[i]==arr[j]&&d[i][j]<d[k][i]+1){ d[i][j]=d[k][i]+1; if (d[i][j]>ans) ans=d[i][j]; } if (ans) return ans+2; else return 0; } };
但是题目要求的数据范围达到了1000,所以$O(n^3)$必然过不了。我们再来考虑优化。很明显我们可以消掉匹配对应$k$的那一层循环,主要有两个思路:
1)因为当$i$和$j$确定时,$arr[k]$的值是已经确定了。又因为$arr[i]$的数据规模可以达到$10^9$,过大了不能直接做一个数组,所以我们用哈希表来寻找这个$k$值。又因为只有$1000$个数,出现哈希冲突的情况比较稀少,所以这样写出来的代码,一般能达到最优时间复杂度$O(n^2)$。
2)因为题目给的数组$arr$是严格递增的,所以我们可以借助这个很不错的性质,做一个二分查找来找$k$值,时间复杂度能达到$O(n^2logn)$。
我写的是哈希:
int d[1007][1007]; vector<int> Map[100003]; int Hash(long long key){ key=(key*97+7)%100003; return (int)key; } int find(int key,vector<int>& arr){ long long h=((long long)key*97+7)%100003; for (int i=0;i<Map[h].size();i++) if (arr[Map[h][i]]==key) return Map[h][i]; return -1; } class Solution { public: int lenLongestFibSubseq(vector<int>& arr) { for (int i=0;i<100003;i++) Map[i].clear(); int lim=arr.size(); int ans=0; for (int i=0;i<lim;i++){ Map[Hash((long long)arr[i])].push_back(i); for (int j=0;j<lim;j++) d[i][j]=0; } for (int j=2;j<lim;j++) for (int i=1,to;i<j;i++){ to=find(arr[j]-arr[i],arr); if (0<=to&&to<i&&d[i][j]<d[to][i]+1){ d[i][j]=d[to][i]+1; if (d[i][j]>ans){ ans=d[i][j]; } } } if (ans) return ans+2; else return 0; } };


浙公网安备 33010602011771号