Problem
Given an array with a length of N, find the longest monotonic auto-incrementing subsequence (not necessarily continuous, but the sequence cannot be messy ). For example, if an array a {5, 6, 7, 1, 2, 8} with a length of 6 is given, the longest monotonic increasing subsequence is {5, 6, 7, 8 }, the length is 4.
Solution 1: Longest Common subsequence Method
This problem can be converted to the longest common subsequence problem. For example, if array a {5, 6, 7, 1, 2, 8} In the example, we sort the array to obtain array a' {1, 2, 5, 6, 7, 8 }, find out the longest common subsequences of arrays A and. Obviously, the longest common subsequence here is {5, 6, 7, 8}, that is, the longest incrementing subsequence of the original array. The longest common subsequence algorithm is described in detail in the introduction to algorithms.
Assume that the two sequences are x = {x1, x2 ,..., xm} and Y = {y1, Y2 ,..., YN), and set Z = {Z1, Z2 ,..., ZK} is any LCS of X and Y.
1) if XM = YN, zk = XM = YN, And the Zk-1 is an lcs of the Xm-1 and Yn-1.
2) If XM! = YN, then ZK! = XM contains Z is a Xm-1 and Y gets an LCS.
3) If XM! = YN, then ZK! = YN contains Z is an LCS of x and Yn-1.
Solution 2: Dynamic Programming Method (time complexity O (N ^ 2 ))
Set the length of N to {A0, A1, A2 ,... an-1), if the length of the longest incrementing sub-sequence ending with AJ is L (J), then l (j) = {max (L (I )) + 1, I <j and a [I] <A [J]}. That is to say, we need to traverse all the positions I (from 0 to J-1) before J to find the L (I) that satisfies Condition A [I] <A [J ), obtain the value of max (L (I) + 1, that is, L (j. Finally, we traverse all L (j) (from 0 to N-1) and find that the maximum is the largest incremental subsequence. The time complexity is O (n ^ 2 ).
For example, if the given array is {5, 6, 7, 1, 2, 8}, L (0) = 1, L (1) = 2, L (2) = 3, L (3) = 1, L (4) = 2, L (5) = 4. Therefore, the maximum length of the Child sequence increasing in this array is 4, and the sequence is {5, 6, 7, 8 }. The algorithm code is as follows:
CPP Code
- # Include <iostream>
- Using namespace STD;
- # Define Len (A) (sizeof (a)/sizeof (A [0]) // array Length
- Int Lis (INT arr [], int Len)
- {
- Int longest [Len];
- For (INT I = 0; I <Len; I ++)
- Longest [I] = 1;
- For (Int J = 1; j <Len; j ++ ){
- For (INT I = 0; I <j; I ++ ){
- If (ARR [J]> arr [I] & longest [J] <longest [I] + 1) {// note that the longest [J] <longest [I] + 1 condition cannot be omitted.
- Longest [J] = longest [I] + 1; // calculates the length of the longest incrementing subsequence ending with arr [J ].
- }
- }
- }
- Int max = 0;
- For (Int J = 0; j <Len; j ++ ){
- Cout <"Longest [" <j <"] =" <longest [J] <Endl;
- If (longest [J]> MAX) max = longest [J]; // find the maximum value from longest [J ].
- }
- Return Max;
- }
- Int main ()
- {
- Int arr [] = {1, 4, 5, 6, 2, 3, 8}; // test Array
- Int ret = Lis (ARR, Len (ARR ));
- Cout <"Max increment substring Len =" <RET <Endl;
- Return 0;
- }
Solution 3: O (nlgn) Algorithm
Suppose there is a sequence d [1 .. 9] = {2, 1, 5, 3, 6, 4, 8, 9, 7}, we can see that its LIS length is 5.
Next, we will try to find it.
We define a sequence B, and then let I = 1 to 9 examine the sequence one by one.
In addition, we use a variable Len to record the maximum number of records.
First, put d [1] In B order, so that B [1] = 2, that is, when there is only 1 digit 2, the minimum end of LIS with a length of 1 is 2. Then Len = 1
Then, Put d [2] in B order, so that B [1] = 1, that is, the minimum end of LIS with a length of 1 is 1, d [1] = 2 is useless. It's easy to understand. Then Len = 1
Then, d [3] = 5, d [3]> B [1], so that B [1 + 1] = B [2] = d [3] = 5, that is to say, the minimum end of LIS with a length of 2 is 5, which is easy to understand. At this time, B [1 .. 2] = 1, 5, Len = 2
Again, d [4] = 3, which is exactly between and 5. It is obviously not suitable to place it at the position of 1, because 1 is smaller than 3, and the minimum end of LIS with a length of 1 should be 1, it is easy to infer that the minimum end of LIS with a length of 2 is 3, so 5 can be eliminated. At this time, B [1 .. 2] = 1, 3, Len = 2
Continue, d [5] = 6, it is behind 3, because B [2] = 3, and 6 is behind 3, so it is easy to know that B [3] = 6, B [1 .. 3] = 1, 3, 6. Is it easy to understand? Len = 3.
6th, d [6] = 4. You can see it is between 3 and 6, so we can replace 6 and get B [3] = 4. B [1 .. 3] = 1, 3, 4, Len continues to be 3
7th, d [7] = 8, it's big, bigger than 4, um. So B [4] = 8. Len becomes 4
8th, d [8] = 9, and B [5] = 9, um. Len continues to increase to 5.
The last one, d [9] = 7, is between B [3] = 4 and B [4] = 8, so we know that the latest B [4] = 7, B [1 .. 5] = 1, 3, 4, 7, 9, Len = 5.
So we know that the LIS length is 5.
Note that this 1, 3, 4, 7, 9 is not Lis, it is only the minimum end of the storage of the corresponding length Lis.With this end, We can insert data one by one. Although the last d [9] = 7 is meaningless for this group of data, if there are two numbers 8 and 9, then we can update 8 to d [5], 9 to d [6], and obtain that the LIS length is 6.
Then we should find one thing: inserting data in B is ordered, and it is replaced without moving-that is, we can use binary search, optimize the insertion time of each number to O (logn )~~~~~ So the time complexity of the algorithm is reduced to O (nlogn )~!
The Code is as follows (array B in the Code starts to store data from position 0 ):
CPP Code
- # Include <stdio. h>
- # Include <stdlib. h>
- # Include <string. h>
- # Define N 9 // Number of array elements
- Int array [N] = {2, 1, 6, 3, 5, 4, 8, 7, 9}; // original array
- Int B [N]; // an array used in dynamic planning. It is used to record intermediate results. The meaning is ambiguous. For more information, see the blog post.
- Int Len; // indicates the number of elements in array B.
- Int Lis (int * array, int N); // calculates the length of the longest incrementing sub-sequence and calculates the elements of array B. After array [] loops are completed, the length of B is Len.
- Int bisearch (int * B, int Len, int W); // modified Binary Search Algorithm
- Int main ()
- {
- Printf ("Lis: % d \ n", Lis (array, n ));
- Int I;
- For (I = 0; I <Len; ++ I)
- {
- Printf ("B [% d] = % d \ n", I, B [I]);
- }
- Return 0;
- }
- Int Lis (int * array, int N)
- {
- Len = 1;
- B [0] = array [0];
- Int I, Pos = 0;
- For (I = 1; I <n; ++ I)
- {
- If (array [I]> B [len-1]) // insert directly to the end of array B if it is greater than the largest element in array B
- {
- B [Len] = array [I];
- ++ Len;
- }
- Else
- {
- Pos = bisearch (B, Len, array [I]); // binary search for the location to be inserted
- B [POS] = array [I];
- }
- }
- Return Len;
- }
- // Modify the binary search algorithm to return the position where the array elements need to be inserted.
- Int bisearch (int * B, int Len, int W)
- {
- Int left = 0, Right = len-1;
- Int mid;
- While (left <= right)
- {
- Mid = left + (right-left)/2;
- If (B [Mid]> W)
- Right = mid-1;
- Else if (B [Mid] <W)
- Left = Mid + 1;
- Else // if this element is found
- Return mid;
- }
- Return left; // if this element does not exist in array B, the position of the element to be inserted is returned.
- }