Problem
Solution
题意为给定一个数组,要你求最长递增子序列(PS子序列是可以不连续的,子串则必须连续)
动态规划解法,dp[i]为以i为结尾的最长递增子序列长度,那么对于0<=j<i,若nums[i]>nums[j],则i位置可以接在j位置后组成递增子序列,状态转移方程为dp[i]=max(dp[i],dp[j]+1)
最后再遍历整个dp数组,找出最长递增子序列的值。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len=nums.size();
if(!len) return 0;
vector<int> dp(len,1);
for(int i=1;i<len;++i){
for(int j=0;j<i;++j){
if(nums[i]>nums[j])
dp[i]=dp[i]>(dp[j]+1)?dp[i]:(dp[j]+1);
}
}
int ans=1;
for(auto x:dp) ans=ans>x?ans:x;
return ans;
}
};
题目提示可以用nlogn的时间复杂度解决LIS问题,查阅资料后了解到:
其实最长递增子序列和一种叫做 patience game 的纸牌游戏有关
首先,给你一排扑克牌,我们像遍历数组那样从左到右一张一张处理这些扑克牌,最终要把这些牌分成若干堆。
处理这些扑克牌要遵循以下规则:
只能把点数小的牌压到点数比它大的牌上。如果当前牌点数较大没有可以放置的堆,则新建一个堆,把这张牌放进去。如果当前牌有多个堆可供选择,则选择最左边的堆放置。
为什么遇到多个可选择堆的时候要放到最左边的堆上呢?因为这样可以保证牌堆顶的牌有序
按照上述规则执行,可以算出最长递增子序列,牌的堆数就是最长递增子序列的长度
基于上述过程,就可以写出用二分查找解决LIS问题的代码
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len=nums.size();
if(!len) return 0;
vector<int> top(len);
int piles=0;
for(int i=0;i<len;++i){
int p=nums[i];
int lo=0,hi=piles;
while(lo<hi){
int mid=(lo+hi)>>1;
if(top[mid]>=p) hi=mid;
else if(top[mid]<p) lo=mid+1;
}
if(lo>=piles) piles++;
top[lo]=p;
}
return piles;
}
};
Result