Longest Increasing Subsequence
- 問題描述:計算一個數組的最長上升子序列。這個子序列內的元素不一定要是相鄰的。比如說數組{1, 100, 2, 3, 4}的LIS就爲{1, 2, 3, 4}
- 解法1-動態規劃
- 首先我們將問題劃分成子問題=>我們該序列一定是以某一個點爲起點的。所有我們另dp[i]表示以i爲起點的LIS長度。
- 構造父問題的解。假設說我們已經知道了dp[i],那麼我們如何構造dp[i-1]呢?注意,這裏我們構造的是dp[i-1],而不是dp[i+1].針對dp[i-1]的所能得到的所有結果就是,nums[i-1] < nums[j] 所有j點中的最大值+1或者就是本身的dp[i-1] 即忽略該點。
- 總結下來dp[i] = max(dp[i], dp[j]+1) j∈[i+1,n) and nums[j]>nums[i]
- 代碼:
int lengthOfLISV2(vector<int>& nums) {
int size = (int) nums.size();
if(size == 0)
return 0;
int dp[size];
memset(dp, 0, sizeof(dp));
int res = 0;
for(int i=size-1;i>=0;i--){
for(int j=i+1;j<size;j++){
if(nums[i] <= nums[j]){
dp[i] = max(dp[i], dp[j] + 1);
res = max(res, dp[i]);
}
}
}
return res + 1;
}
- 解法2:O(NlogN):
- 參照1, 2
- 我們新建一個數組B,保存可能是最後subsequence的數字。
- 我們遍歷輸入數組A中的每個數組,針對該數字a,我們在B中找到小於它的最大數字
- 如果B中所有數字都小於a,則我們直接將a加入B中,代表我們從開始的點,到當前數字a能生成的LIS長度一定會增加1。
- 如果B中所有數字都大於a, 則我們B中的第一個數字置換成a,代表我們可能會從新開始一個新的subsequence。⚠️我們並沒有改變B的大小。所有最後的LIS的長度不會改變。
- 如果a在B中間,我們將B中最大的小於a的元素替換成a。道理和上一種情況是一樣的。
- 最後B的大小,就是我們所能返回LIS的長度。
- 概括來說,當由SubArray[:i-1]構造SubArray[:i]的解時候,如果nums[i]在SubArray[:i-1]對應的Subsequence之間的話,則我們把其中最大的小於nums[i]的替換成nums[i]。因爲原來的數字已經沒有用了,它起的作用完全可以由nums[i]代替,所以我們可以將其替換掉。
- 代碼
int lengthOfLISV3(vector<int>& nums) {
vector<int> saved_num;
for(auto n: nums){
auto it = lower_bound(saved_num.begin(), saved_num.end(), n);
if(it == saved_num.end()){
saved_num.push_back(n);
}else{
*it = n;
}
}
return saved_num.size();
}