lis lcs 复杂度
2014-11-12 21:06
211 查看
最长公共子序列 的 nlogn 的算法本质是 将该问题转化成 最长增序列(LIS),因为 LIS 可以用nlogn实现,所以求LCS的时间复杂度降低为 nlogn。
1. 转化:将LCS问题转化成LIS问题。
假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。
记录s1中每个元素在s2中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:
loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。
将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。
在对s3求LIS得到的值即为求LCS的答案。(这点我也只是大致理解,读者可以自己理解甚至证明。)
2.求LIS的 nlogn 的算法:
参考上面给出链接中的pdf,由于是英文的,我也只是做一些翻译,译得不准请见谅及指正。
覆盖:是序列s的几个不相交的降序列,它们包含了s中的所有元素,降序列的个数为c。
最小覆盖:c值最小的覆盖。
定理:序列s的最长增序列等于最小覆盖。
于是:求s的最长增序列转化成求s的最小覆盖。
3.求最小覆盖的 nlogn 的算法。
上图来自链接中的pdf,其中 (i, j)表示序列中第 j 个降序列的最后一个元素是 i 。可用以为数组a实现这个记录。
初始化,a[ 1 ] = s[ 1 ]。
对序列s中第i个元素进行处理时,都尽量将这个元素加到之前的降序列中最后一个元素最小的那个降序列的后面(类似贪心的思想),可保证求得的是最小覆盖,由图可知之前的降序列的最后一个元素是升序排列的,此时可以用二分搜索最后一个元素最小的且大于等于元素i的降序列,将元素i加到这个序列后面。
当然,若没有这样的序列,就再建一个降序列,目前的最后一个元素为元素i。
下面是一道题
题目链接:http://icpc.njust.edu.cn/Contest/Show/41
代码附上:
[cpp] view
plaincopy
#include <iostream>
#include <stdio.h>
#include <memory.h>
using namespace std;
#define LEN 100005
int a[LEN], b[LEN];
int loc[LEN], n;
void calLoc()
{
int i;
for(i = 1; i <= n; i++)
loc[b[i]] = i;
}
int LIS()
{
int i, k, l, r, mid;
a[1] = b[1], k = 1;
for(i = 2; i <= n; i++)
{
if(a[k] < b[i]) a[++k] = b[i];
else {
l = 1; r = k;
while(l <= r)
{
mid = ( l + r ) / 2;
if(a[mid] < b[i])
l = mid + 1;
else
r = mid - 1;
}
a[l] = b[i];
}
}
return k;
}
int main()
{
int i;
while(scanf("%d", &n) != EOF)
{
for(i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(i = 1; i <= n; i++)
scanf("%d", &b[i]);
calLoc();
for(i = 1; i <= n; i++)
b[i] = loc[a[i]];
printf("%d\n", LIS());
}
return 0;
}
1. 转化:将LCS问题转化成LIS问题。
假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。
记录s1中每个元素在s2中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:
loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。
将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。
在对s3求LIS得到的值即为求LCS的答案。(这点我也只是大致理解,读者可以自己理解甚至证明。)
2.求LIS的 nlogn 的算法:
参考上面给出链接中的pdf,由于是英文的,我也只是做一些翻译,译得不准请见谅及指正。
覆盖:是序列s的几个不相交的降序列,它们包含了s中的所有元素,降序列的个数为c。
最小覆盖:c值最小的覆盖。
定理:序列s的最长增序列等于最小覆盖。
于是:求s的最长增序列转化成求s的最小覆盖。
3.求最小覆盖的 nlogn 的算法。
上图来自链接中的pdf,其中 (i, j)表示序列中第 j 个降序列的最后一个元素是 i 。可用以为数组a实现这个记录。
初始化,a[ 1 ] = s[ 1 ]。
对序列s中第i个元素进行处理时,都尽量将这个元素加到之前的降序列中最后一个元素最小的那个降序列的后面(类似贪心的思想),可保证求得的是最小覆盖,由图可知之前的降序列的最后一个元素是升序排列的,此时可以用二分搜索最后一个元素最小的且大于等于元素i的降序列,将元素i加到这个序列后面。
当然,若没有这样的序列,就再建一个降序列,目前的最后一个元素为元素i。
下面是一道题
题目链接:http://icpc.njust.edu.cn/Contest/Show/41
代码附上:
[cpp] view
plaincopy
#include <iostream>
#include <stdio.h>
#include <memory.h>
using namespace std;
#define LEN 100005
int a[LEN], b[LEN];
int loc[LEN], n;
void calLoc()
{
int i;
for(i = 1; i <= n; i++)
loc[b[i]] = i;
}
int LIS()
{
int i, k, l, r, mid;
a[1] = b[1], k = 1;
for(i = 2; i <= n; i++)
{
if(a[k] < b[i]) a[++k] = b[i];
else {
l = 1; r = k;
while(l <= r)
{
mid = ( l + r ) / 2;
if(a[mid] < b[i])
l = mid + 1;
else
r = mid - 1;
}
a[l] = b[i];
}
}
return k;
}
int main()
{
int i;
while(scanf("%d", &n) != EOF)
{
for(i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(i = 1; i <= n; i++)
scanf("%d", &b[i]);
calLoc();
for(i = 1; i <= n; i++)
b[i] = loc[a[i]];
printf("%d\n", LIS());
}
return 0;
}
相关文章推荐
- LCS LIS LCIS 算法
- hdu 1423(LCS+LIS)
- Dynamic Programming 学习笔记(二) LIS vs LCS
- [LCS][LIS]Uva10635]
- 最长公共子序列(LCS)和最长递增子序列(LIS)的求解
- hdu4545--暴力/水dp--LCS/LIS真的是个好东西
- LIS(最长递增子序列)和LCS(最长公共子序列)的总结
- LCS与LIS
- LIS(最长的序列)和LCS(最长公共子)总结
- 最长子序列(LCS, LIS, LCIS)
- nyoj See LCS again 760 (LCS&&LIS) 好题
- 最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)
- BZOJ 1264 基因匹配Match(LCS转化LIS)
- LCS?LIS
- 九度1131_合唱队形【LIS】【LCS】
- UVa 10635 王子和公主(LCS转LIS)
- LCS转为LIS
- 算法(一) --DP动态规划(LIS和LCS)
- LIS && LCS && LCIS && LPS && MCS模板
- 动态规划不经典问题之LCS与LIS的终极合体——LCIS!