300. Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
方法一:暴力枚举
TC: O(2^n)
类似于subsets的思路,只不过在递归调用前需要判断是否符合递增的条件。
TLE : 21/24 test cases passed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : int lengthOfLIS (vector <int >& nums) { if (nums.empty()) return 0 ; vector <int > cur; maxLen = 1 ; for (int i=1 ; i<=nums.size(); ++i) dfs(nums, i, 0 , cur); return maxLen; } private : int maxLen; void dfs (vector <int >& nums, const int n, int s, vector <int >& cur) { if (cur.size() == n) { maxLen = max(maxLen, n); return ; } for (int i=s; i<nums.size(); ++i) { if (!cur.empty() && nums[i] <= cur.back()) continue ; cur.push_back(nums[i]); dfs(nums, n, i+1 , cur); cur.pop_back(); } } };
方法二:记忆化递归
LIS([10,9,2,5,3,7,101,18]) = max {
LIS([10,9,2,5,3,7]) + 1,
LIS([10,9,2,5,3]) + 1,
LIS([10,9,2,5]) + 1,
LIS([10,9,2]) + 1,
LIS([10,9]) + 1,
LIS([10]) + 1
}
递归终止条件:
LIS([10,9,2]) = 1 //不存在比2小的数
LIS([10]) = 1 //单个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : int lengthOfLIS (vector <int >& nums) { int n = nums.size(); if (n == 0 ) return 0 ; f_ = vector <int >(n, 0 ); int ans = 0 ; for (int i = 0 ; i < n; ++i) ans = max(ans, LIS(nums, i)); return ans; } private : vector <int > f_; int LIS (const vector <int >& nums, int r) { if (r == 0 ) return 1 ; if (f_[r] > 0 ) return f_[r]; int ans = 1 ; for (int i = 0 ; i < r; ++i) if (nums[r] > nums[i]) ans = max(ans, LIS(nums, i) + 1 ); f_[r] = ans; return f_[r]; } };
方法三:动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int lengthOfLIS (vector <int >& nums) { if (nums.empty()) return 0 ; vector <int > f(nums.size(),1 ); for (int i=0 ; i<nums.size(); i++) for (int j=0 ; j<i; j++) if (nums[j] < nums[i]) f[i] = max(f[i],f[j]+1 ); return *max_element(f.cbegin(), f.cend()); } };
方法四:DP + Binary search
使用dp[i]保存所有长度为i+1的递增子序列中末尾元素的最小值。
注意最后v中存储的值并不一定是最长递增子序列,只是他们的长度相等。
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : int lengthOfLIS (vector <int >& nums) { vector <int > v; for (auto a : nums) { auto it = lower_bound(v.begin(), v.end(), a); if (it == v.end()) v.push_back(a); else *it = a; } return v.size(); } };
Follow up
求出所有最长的LIS数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <vector> #include <unordered_set> #include <unordered_map> using namespace std ;class Solution {public : vector <vector <int >> LIS(vector <int >& nums) { vector <int > cur; vector <vector <int >> ans; for (int i=0 ; i<=nums.size(); ++i) LIS(nums, i, 0 , cur, ans); return ans; } private : int maxLIS = 1 ; void LIS (const vector <int >& nums, const int n, int s, vector <int >& cur, vector <vector <int >>& ans) { if (cur.size() == n) { if (!ans.empty() && cur.size() > ans[0 ].size()) ans.clear(); ans.push_back(cur); return ; } for (int i=s; i<nums.size(); ++i) { if (!cur.empty() && nums[i] <= cur.back()) continue ; cur.push_back(nums[i]); LIS(nums, n, i+1 , cur, ans); cur.pop_back(); } } }; int main () { vector <int > nums{10 , 9 , 2 , 5 , 3 , 7 , 101 , 18 }; vector <vector <int >> ans; ans = Solution().LIS(nums); for (auto num : ans){ for (auto e : num) cout << e << " " ; cout << endl ; } }
求最长递增子数组的个数
Lintcode 1093 最长升序子序列个数
仍然使用动态规划维护两个数组:
dp[i]表示以nums[i]为结尾的LIS的长度,用cnt[i]表示以nums[i]为结尾的最长LIS的个数。
遍历数组,对于nums[i], 再去遍历nums[j], 如果nums[i] <= nums[j], continue;
否则的话,更新dp[i]的规则不变;
更新cnt[i]的规则:
如果dp[j]+1==dp[i], cnt[i]+=cnt[j];//相当于最长LIS的长度没变,但是个数增加
如果dp[j]+1 > dp[i],则cnt[i] = cnt[j].//相当于最长LIS的长度变长了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution {public : int findNumberOfLIS (vector <int > &nums) { if (nums.empty()) return 0 ; const int n = nums.size(); vector <int > dp(n,1 ); vector <int > ans(n,1 ); int max_ans = 1 ; for (int i=1 ; i<n; ++i) { for (int j=0 ; j<i; ++j) { if (nums[i] <= nums[j]) continue ; else if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1 ; ans[i] = ans[j]; }else if (dp[j] + 1 == dp[i]) ans[i] += ans[j]; } max_ans = max(max_ans, dp[i]); } int cnt = 0 ; for (int i=0 ; i<n; ++i) if (dp[i]==max_ans) cnt += ans[i]; return cnt; } };
Longest Continuous Increasing Subsequence
这个相当于是连续递增的subarray.
本质上是一道动态规划的题目:
定义dp[i]为以num[i]结尾的最长连续递增子序列(LCIS), 要求num[i]必须使用。
状态转移方程:
if num[i] > num[i-1]:
dp[i] = dp[i-1] + 1
else
dp[i] = 1
最终答案存储在max(dp[i]), 0<=i<=n中。
可以降维,使用O(1)的空间;另外在循环的过程中就更新max_LCIS, 避免最后统一更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int findLengthOfLCIS (vector <int >& nums) { if (nums.empty()) return 0 ; int cur = 1 ; int ans = 1 ; for (int i=1 ; i<nums.size(); ++i) { if (nums[i] > nums[i-1 ]) { cur++; ans = max(ans, cur); }else cur = 1 ; } return ans; } };