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

 

转自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 all i < k such that ai <ak. If this set is null, then all of the elements that come before  ak are greater than it, which forces qk = 1. Otherwise, ifSk is not null, then q has some distribution over Sk. By the general contract of q, if we maximize q overSk, we get the length of the longest increasing subsequence inSk; we can append ak 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 that Ai,j < ai + 1 < =Ai,j + 1 and the length will be j + 1.

Notice that in this case, Ai + 1,j + 1 will be equal toai + 1, and all Ai + 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 continue
		if (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 Vector
	vector<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;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值