longest increasing subsequence (最长递增子序列)

来源:互联网 发布:淘宝有找兼职的吗 编辑:程序博客网 时间:2024/05/16 06:27

 

转自algorithmist  原文链接 http://www.algorithmist.com/index.php/Longest_Increasing_Subsequence

The Longest Increasing Subsequence problem is to find the longest increasing subsequence of a given sequence. It also reduces to aGraph Theory problem of finding the longest path in aDirected acyclic graph.

Overview

Formally, the problem is as follows:

Given a sequence , find the largest subset such that for everyi <j,ai < aj.

Techniques

Longest Common Subsequence

A simple way of finding the longest increasing subsequence is to use theLongest Common Subsequence (Dynamic Programming) algorithm.

1.       Make a sorted copy of the sequenceA, denoted asB.O(nlog(n)) time.

2.       Use Longest Common Subsequence on with A andB. O(n2) time.

Dynamic Programming

There is a straight-forward Dynamic Programming solution in O(n2) time. Though this is asymptotically equivalent to theLongest Common Subsequence version of the solution, the constant is lower, as there is less overhead.

Let A be our sequence . Define qk as the length of the longest increasing subsequence of A, subject to the constraint that the subsequence must end on the elementak. The longest increasing subsequence of A must end on some element of A, so that we can find its length by searching for the maximum value of q. All that remains is to find out the valuesqk.

But qk can be found recursively, as follows: consider the setSk of alli < k such that ai <ak. If this set is null, then all of the elements that come before  ak are greater than it, which forcesqk = 1. Otherwise, ifSk is not null, then q has some distribution overSk. By the general contract of q, if we maximize q overSk, we get the length of the longest increasing subsequence inSk; we can appendak to this sequence, to get that:

If the actual subsequence is desired, it can be found in O(n) further steps by moving backward through the q-array, or else by implementing the q-array as a set of stacks, so that the above "+ 1" is accomplished by "pushing"ak into a copy of the maximum-length stack seen so far.

Some pseudo-code for finding the length of the longest increasing subsequence:

function lis_length( a )    n := a.length    q := new Array(n)    for k from 0 to n:        max := 0;        for j from 0 to k, if a[k] > a[j]:            if q[j] > max, then set max = q[j].        q[k] := max + 1;    max := 0    for i from 0 to n:        if q[i] > max, then set max = q[i].    return max;


 

Faster Algorithm

There's also an O(nlogn) solution based on some observations. LetAi,j be the smallest possible tail out of all increasing subsequences of lengthj using elements .

Observe that, for any particular i, . This suggests that if we want the longest subsequence that ends withai + 1, we only need to look for a j such thatAi,j < ai + 1 < =Ai,j + 1 and the length will bej + 1.

Notice that in this case, Ai + 1,j + 1 will be equal toai + 1, and allAi + 1,k will be equal toAi,k for .

Furthermore, there is at most one difference between the set Ai and the set Ai + 1, which is caused by this search.

Since A is always ordered in increasing order, and the operation does not change this ordering, we can do abinary search for every single .

Further explain:--GONG Zhi Tao 11:19, 1 August 2012 (EDT)

We have elements: .

And we have a longest increasing subsequences of them: , for any Ai,k(1 < = k < = j) you could not find a smaller alternative.

Now we have a new element: ai + 1

What we can do about it:

1. insert it at the back if Ai,j <ai + 1, where we will have a longer one;

2. make it an alternative for Ai,k if

Alternative means that we MIGHT get longer ones if using the new element.

 

 

C++ implementation

#include <vector>using namespace std; /* Finds longest strictly increasing subsequence. O(n log k) algorithm. */void find_lis(vector<int> &a, vector<int> &b){vector<int> p(a.size());int u, v; if (a.empty()) return; b.push_back(0); for (size_t i = 1; i < a.size(); i++)         {                // If next element a[i] is greater than last element of current longest subsequence a[b.back()], just push it at back of "b" and continueif (a[b.back()] < a[i])                 {p[i] = b.back();b.push_back(i);continue;}                 // Binary search to find the smallest element referenced by b which is just bigger than a[i]                // Note : Binary search is performed on b (and not a). Size of b is always <=k and hence contributes O(log k) to complexity.    for (u = 0, v = b.size()-1; u < v;)                 {int c = (u + v) / 2;if (a[b[c]] < a[i]) u=c+1; else v=c;}                 // Update b if new value is smaller then previously referenced value if (a[i] < a[b[u]])                 {if (u > 0) p[i] = b[u-1];b[u] = i;}} for (u = b.size(), v = b.back(); u--; v = p[v]) b[u] = v;} /* Example of usage: */#include <cstdio>int main(){int a[] = { 1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 };vector<int> seq(a, a+sizeof(a)/sizeof(a[0])); // seq : Input Vectorvector<int> lis;                              // lis : Vector containing indexes of longest subsequence         find_lis(seq, lis);         //Printing actual output for (size_t i = 0; i < lis.size(); i++)printf("%d ", seq[lis[i]]);        printf("\n");     return 0;}