The longest ascending subsequence (sequence length + sequence output)

A, defines the LIS:
longest rising sequence (Longest Increasing Subsequence), referred to the LIS, it is required in some cases also the longest non-descending sequence , 二者区别就是序列中是否可以有相等的数. Suppose we have a sequence bi b_ibi,当 b 1 < b 2 < … < b S b_1 < b_2 < … < b_S b1<b2<<bSWhen we call this sequence ascending. For a given sequence (a1, a2, …, aN), we can also get some ascending subsequences (ai 1, ai 2,…, ai K) (a_{i_1}, a_{i_2},… , a_{i_K})(ai1,ai2,,aiK),这里 1 < = i 1 < i 2 < … < i K < = N 1 <= i_1 < i_2 < … < i_K <= N 1<=i1<i2<<iK<=N , but must follow the order from front to back. For example, for the sequence (1, 7, 3, 5, 9, 4, 8), we will get some ascending subsequences, such as (1, 7, 9), (3, 4, 8), (1, 3, 5, 8) and so on, and the longest of these subsequences (such as subsequence (1, 3, 5, 8)), its length is 4, so the longest ascending subsequence length of this sequence is 4 .
First of all, we need to know the concept of substrings and subsequences. Let’s take character substrings and character subsequences as examples, which is more vivid and can also understand the substrings and subsequences of characters along the way:
(1) Character substrings refer to strings. The consecutive n characters in abcdefg, ab, cde, fg, etc. belong to its substring.
(2) The character subsequence refers to n characters in the string that are not necessarily consecutive but in the same sequence, that is, part of the characters in the string can be removed, but the sequence cannot be changed. For example, in abcdefg, acdg and bdf belong to its subsequences, while bac and dbfg are not, because they are inconsistent with the character sequence of the string.
Knowing this, the sub-sequence of the value is easy to understand, that is, the sub-sequence composed of numbers. In this case, the longest ascending subsequence is also easy to understand. In the final analysis, it is still a subsequence. Then, the longest ascending subsequence in the subsequence is our longest ascending subsequence, so it sounds easy to understand. friends -
there is a very important question: Do we use a set of perspectives to understand these concepts, sequences, common subsequence and the longest common subsequence is not unique, but it is clear that, for a fixed array, although not LIS sequence It must be unique, but the length of LIS is unique. Take the chestnut we just mentioned, given the sequence (1, 7, 3, 5, 9, 4, 8), the length of the longest ascending subsequence is 4, which is certain, but the sequence can be ( 1, 3, 5, 8), or (1, 3, 5, 9).
2. Solution
General solution: dynamic programming dp
dp[i] represents the length of the longest ascending subsequence ending in a[i]
Obviously dp[i]=max(dp[i],dp[j]+1) satisfies a[i]>a[j],1<=i<n,j<i The
time complexity of the realization process is O( n^2)
Code: The complexity is O(n^2)

import java.util.Scanner;
public class Main{
    
    
    public static void main(String[] args){
    
    
        Scanner cin=new Scanner(System.in);
        while(cin.hasNext()){
    
    
            int n=cin.nextInt();
            int a[]=new int[n+1];
            int dp[]=new int[n+1];//dp[i]表示前i个数以a[i]结尾的最长上升子序列长度
            int pos=0;//记录最后一次更新的最长上升子序列长度最后位置
            for(int i=0;i<n;i++){
    
    
                a[i]=cin.nextInt();
                dp[i]=1;
            }
            int ans=1;//记录答案
            for(int i=0;i<n;i++){
    
    
                for(int j=0;j<i;j++){
    
    
                    if(a[j]<a[i]) dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            for(int i=0;i<n;i++){
    
    
                if(ans<=dp[i]){
    
     //加个等于记录一下使得最后一个序列数最小
                    ans=dp[i];
                    pos=i;
                }
            }
            System.out.println(ans);//最长上升子序列长度
            int num[]=new int[ans+1];//记录其中一种解决方案,正着记录,倒着输出
            num[0]=a[pos];
            int cnt=0;
            for(int i=pos-1;i>=0;i--){
    
    
                if(dp[i]==ans-1){
    
    
                    num[++cnt]=a[i];
                    ans--;
                }
            }
            for(int i=cnt;i>=0;i--){
    
     //输出答案
                if(i==cnt) System.out.print(num[i]);
                else System.out.print(" "+num[i]);
            }
            System.out.println();
        }
    }
}

Greedy + dichotomy: The complexity is in O(nlogn).
Idea: Use a low array to record the length, low[i] represents the minimum value of the LIS ending element that is i after delivery, so when we record low, when a[i] When it is greater than low[++maximum length of current LIS], directly connect a[i] to low, otherwise find the first position pos greater than or equal to the current element a[i] in low, and replace it with a[i] Drop the low[pos] before, so that the recorded low array is not necessarily a subsequence solution . We use an array pos1[] to record the position of the element in a[] in low. Finally, we find the solution that satisfies the subscript of the longest ascending subsequence and record the subsequence. It should be noted that when the lower bound of the binary update is a[mid]<x, it cannot be updated with equals, otherwise an error will occur.
If it is C++, use lower_bound directly . A C++ code is given at the end of the article.
Here is a set of card data.

样例:
88
317 211 180 187 104 285 63 117 320 35 288 299 235 282 105 231 253 74 143 148 77 249 310 137 191 184 88 8 194 12 117 108 260 313 114 261 60 226 133 61 146 297 291 13 198 286 254 96 135 48 135 307 23 155 203 258 168 42 301 45 164 193 26 290 280 172 94 230 156 36 250 174 47 188 148 138 194 89 71 119 218 325 136 63 271 210 320 309
答案:
17
35 74 77 88 108 114 133 135 155 164 172 174 188 194 218 271 309 

Of course, you can verify it in letcode: the longest ascending subsequence , and you can also submit a test in Niuke.com : Redraiment's move

import java.util.Scanner;
public class Main{
    
    
    public static final int INF=0x3f3f3f3f;
    public static void main(String[] args){
    
    
        Scanner cin=new Scanner(System.in);
        while(cin.hasNext()){
    
    
            int n=cin.nextInt();
            int a[]=new int[n+1];
            int low[]=new int[n+1];//low[i]表示长度为i的LIS结尾元素的最小值。
            int pos1[]=new int[n+1];//用pos1数组记录a中元素在low中出现位置
            int ans[]=new int[n+1];//用ans数组记录答案
            for(int i=0;i<n;i++){
    
    
                a[i]=cin.nextInt();
                low[i]=INF;
            }
            int cnt=0;
            low[0]=a[0];pos1[0]=0;
            for(int i=1;i<n;i++){
    
    
                if(a[i]>low[cnt]){
    
    
                    low[++cnt]=a[i];//如果a[i]大于了,就接在后面,否则,二分查找位置替换掉;
                    pos1[i]=cnt;
                }
                else{
    
    
                    int pos=binary_search(low,cnt,a[i]);
                    low[pos]=a[i];
                    pos1[i]=pos;
                }
            }
            System.out.println(cnt+1);
            int maxNum=INF,index=cnt;
            for(int i=n-1;i>=0;i--){
    
    
                if(index==-1) break; //说明找完了
                if(pos1[i]==index&&maxNum>a[i]){
    
    //先找第一个在low数组index位置,再找第一个在low数组index-1位置直到index=-1
                    maxNum=a[i];
                    ans[index--]=i;
                }
            }
            for(int i=0;i<=cnt;i++) System.out.print(a[ans[i]]+" ");
            System.out.println();
        }
    }
    public static int binary_search(int a[],int R,int x){
    
    
        int L=0;
        while(L<=R){
    
    
            int mid=(L+R)>>1;
            if(a[mid]<x) L=mid+1;
            else R=mid-1;
        }
        return L;
    }
}

The code is tested in Niuke.com, and the output sub-sequence solution has not been tested on other OJ platforms. If it is misleading, please advise!

Of course, more solutions and sample questions can be found in: Detailed explanation of the longest ascending subsequence (LIS) + sample template (full)

C++ version LIS length:

#include<bits/stdc++.h>
using namespace std;
const int maxn =300003, INF = 0x7f7f7f7f;
int low[maxn], a[maxn];
int n, ans;
int binary_search(int *a, int R, int x)
//二分查找,返回a数组中第一个>=x的位置
{
    
    
    int L = 1, mid;
    while(L <= R)
    {
    
    
        mid = (L+R) >> 1;
        if(a[mid] < x)
            L = mid + 1;
        else
            R = mid - 1;
    }
    return L;
}

int main()
{
    
    
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
    
    
        scanf("%d", &a[i]);
        low[i] = INF;   //由于low中存的是最小值,所以low初始化为INF
    }
    low[1] = a[1];
    ans = 1;   //初始时LIS长度为1
    for(int i=2; i<=n; i++)
    {
    
    
        if(a[i] > low[ans])    //若a[i]>=low[ans],直接把a[i]接到后面
            low[++ans] = a[i];
        else
        {
    
    
            int j=lower_bound(low+1,low+1+ans,a[i])-low;
            low[j]=a[i];
        }
    }
    printf("%d\n", ans);   //输出答案
    return 0;
}

Guess you like

Origin blog.csdn.net/dl962454/article/details/107434055