Leetcode 最长递增子序列:返回字典序最小

题目描述

给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)

 

输入

9
2 1 5 3 6 4 8 9 7

输出

1 3 4 8 9

 

最长递增子序列问题有两大类思路:

dp[i]=len  表示以[i]结尾的最长递增子序列长度为len=》那么dp[i+1]的计算需要利用0=》i计算结果

min[len]=i  表示长度为len的递增子序列的末尾最小元素为i 注意此时dp数组不一定是实际上从左往右的顺序

对于第一种思路,其求解的时间复杂度为平方级别,而对于第二种思路,每当遇到一个新元素val,二分查找到已有dp数组中第一个比val大的数,更新dp[k]=val

如果问题求最长递增子序列长度,那么直接返回dp数组的长度即可,若问题需要返回该最长递增子序列(可能有多个)  而该问题要求返回字典序最小的数。

【易错点】

直接返回dp[]中的各元素  #dp[]中各元素表示对应长度末尾最小的数  但它们位置上不构成前后关系 不能直接构成递增子序列

例如对于用例,其最终计算的dp[]数组如下:

1 3 4 7 9    其中长度为4末尾最小的数为7 但 7其实在9后面 因此 1 3 4 7 9 不能构成递增子序列

 

 

【正确思路】

  • dp[i]=len 表示以arr[i]结尾的最长递增子序列长度为len
  • min[len]=i 表示长度为len的末尾最小的数为i

那么对于一个新的数val 每一次计算完min[]之后,其dp的值也确定的计算出来,而此时最长的递增子序列长度:

若此时dp[i]>=maxlen => maxlen=dp[i] #注意为>=  后来的arr[i]的字典序一定更小

【反证法】

对于i<j 如果有dp[i]==dp[j] 即以arr[i]  arr[j]结尾的数最长递增子序列长度相等

那么一定有=》 arr[i]>arr[j]  因为如果arr[i]<arr[j]  那么dp[j]=dp[i]+k  

因此后来的arr[]成为字典序更小的数

代码如下: 

int main()
{
    int n;
    cin>>n;
    vector<int> arr(n);
    for(int i=0;i<n;i++)
        cin>>arr[i];
    vector<int> dp(n,1);//dp[i]=k 表示以arr[i]结尾的最长递增子序列长度
    vector<int> end(n+1,0);  //end[k]=i  表示长度为k的递增子序列的末尾最小数为i
    end[1]=arr[0];
    int _max=1;
    int index=0;
    for(int i=1;i<n;i++)
    {
        int val=arr[i];
        int l=1;
        int r=_max;
        while(l<=r)  //二分查找第一个比val大的数
        {
            int mid=(l+r)>>1;
            if(end[mid]<val)
                l=mid+1;
            else
                r=mid-1;
        }
        //end[l]是第一个比val大的数
        end[l]=val;
        dp[i]=l;//以i结尾的数 最长的递增子序列长度为l
        if(l>=_max)  //后来的arr[]总是字典序更小字典序更小
        {
            _max=l;
            index=i;
        }
    }
    vector<int> ans(_max);
    for(int i=index;i>=0;i--)
    {
        if(dp[i]==_max)
        {
            ans[--_max]=arr[i];
          
        }
        
    }
    for(int i=0;i<ans.size();i++)
        cout<<ans[i]<<" ";
    
}

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页