Sequence DP | Longest increasing subsequence

First have to make clear substring and subsequence concept: both are a sequence from left to right to find certain kinds of elements, but the substring must be continuous, sub-sequences need. For example: the string of abcde can be abc, cde, etc., and the subsequence can be abc, ade, ace.


Longest increasing subsequence problem (LIS) : Given a sequence a[] with n elements, find the maximum length of its increasing subsequence.

The short topic revealed a lot of malice... I thought about this question for a long time, and I also referred to a lot of information. After many recollections, I finally mastered it. I wrote down my notes today, and I will forget it next time. It's hard to pick up. In addition to the very violent and time-consuming enumeration algorithm, two implementation methods are summarized below:


1. Dynamic programming-time complexityO (n ^ 2)

Through analysis, for an increasing subsequence s, whether element e can be added to the end of the sequence to increase its length by one depends on whether e is greater than the last element of sequence s. Therefore, for the increasing sequence discussed in this question, the last element of the sequence is very important.

Then the idea is here: dp[ i] = the length of the longest subsequence ending with element a[ i ], then the state transition equation is as follows:

\bg_white dp[i] = max_{0\leq k< i , a[k] <a[i]}(dp[ k ] ) + 1

Initial situation: assign all dp arrays to initial value 1

Then the final answer is \bg_white LIS = max_{1\leq i\leq n}(dp[i]) 


Code:

#include <algorithm>
#include <climits>
#define MAXN 100000
using namespace std;

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 0, dp[MAXN];
        /* 将dp初值全设置为1 */
        for(int i = 0; i < MAXN; i++)
            dp[i] = 1;
        for(int i = 0; i < nums.size(); i++) {
            for(int j = 0; j < i; j++)
                if(nums[j] < nums[i]) 
                    dp[i] = max(dp[i], dp[j] + 1);
            len = max(len, dp[i]);  //更新最大长度
        }
        return len;
    }
};

2. Dynamic programming + binary search-time complexityO (nlogn)

This method is faster than the above, but it is also relatively difficult to understand:

First of all, we must understand: In fact, for a subsequence, we hope that the last element of it is as small as possible, so that it has a chance to become longer. So in fact, when discussing, for the increasing subsequences of equal length, we only need to select the sequence with the smallest element in it and continue the discussion. For example: the sequence "1 3 2 6 9", when I discuss the first 3 items, there are two increasing subsequences of length 2: "1 3" and "1 2", then the next sequence should be "1" Continuation on 2", the "1 3" sequence can be better replaced by it.


Then the idea is here: Take an array tail[i]: the smallest last element of the increasing subsequence of length i (a bit winding, see the example for better understanding), and finally the tail is opened as long as the LIS is more long. And the  tail array must be an increasing sequence ! Initial situation: tail[ 1] = a[ 0 ], len = 1.

The specific algorithm is as follows, sequentially traversing the elements in the array a[ ]:

  • If a[ i] >= tail[ len ]: Since this element is larger than the last element of the current longest subsequence we recorded, we can increase our longest subsequence : len ++, tail[ len] = a [i]
  • Otherwise: then the length of the subsequence cannot be increased, but it can be seen whether the minimum value of the last element of the slightly shorter subsequence can be updated . Use dichotomy (tail array is increasing) to find the subscript k is satisfied tail[k] < a[i] < tail[k+1], and then assign tail[k +1] = a[ i ]. 

see an example below:

 


note:

  1. In the end, the length len developed by the tail is the answer, but the sequence left in the tail is not necessarily our longest increasing subsequence. It can only be said that this method can calculate the length, but it cannot give the corresponding answer sequence.
  2. The time complexity of binary search is O (logn)

Code:

#include <algorithm>
#include <climits>
#define MAXN 100000
using namespace std;

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        /* 防止nums为空的情况导致RE */
        if(nums.size() == 0)
            return 0;
        int tail[MAXN] = {0}, len = 1; 
        tail[1] = nums[0];
        for(int i = 1; i < nums.size(); i++) {
            if(nums[i] > tail[len])
                tail[++len] = nums[i];
            else {
                int index = Find(tail, 0, len, nums[i]); //二分查找
                tail[index] = nums[i];  //赋值
            }    
        }
        return len;
    }
    /* 在数组a[l..r]中找到 a[k - 1] < temp <= a[k] 
       返回k */
    int Find(int a[], int l, int r, int temp) {
        if(r - l == 1)
            return r;
        int mid = (l + r) / 2;


        if(temp == a[mid])
            return mid;
        else if(temp < a[mid])
            return Find(a, l, mid, temp);
        else 
            return Find(a, mid, r, temp);    
    }
};


If you have any questions, please comment and exchange. If this article is helpful to you, please like it, hehe~ 


end 

Welcome to pay attention to the personal public account "  Chicken Wing Programming" , here is a serious and well-behaved code farmer.

---- Be the most well-behaved blogger and the most solid programmer ----

Aim to write each article carefully, and usually summarize the notes into push updates~

Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_43787043/article/details/105226674