码迷,mamicode.com
首页 > 其他好文 > 详细

最长公共子序列 nlogn

时间:2016-05-01 23:07:08      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:

 

先来个板子

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+20, M = 1e6+10, mod = 1e9+7, inf = 1e9+1000;
typedef long long ll;

struct node
{
    int c;
    int num;
} u[N];

int i,j,k = 0,n,m,x,y = 0,T = 0,ans = 0,big = 0,cas  = 0,num = 0,len = 0;
bool flag = 0;

bool cmp(node a,node b)
{
    if (a.c==b.c) return a.num>b.num;
    return a.c<b.c;
}

vector <int> p;
int a[N],b[N],c[N];
int lena,lenb,dp[N];

int main()
{
    scanf("%d%d",&lena,&lenb);
    for(int i=0;i<lena;i++) scanf("%d",&a[i]);
    for(int i=0;i<lenb;i++) scanf("%d",&b[i]);
    for (i=0;i<lenb;i++)
    {
         u[i].c=b[i];
         u[i].num=i;
    }
    sort(u,u+lenb,cmp);//对b串排序
    for (i=0;i<lenb;i++)//排序后存入字符串c中,便于使用lower_bound
    {
        c[i]=u[i].c;
    }
    c[lenb]=1e9+10;
     for (i=0;i<lena;i++)//计算A中每个元素在B中的序号
     {
         k=lower_bound(c,c+lenb,a[i])-c;
         while (k<lenb && a[i]==c[k])
         {
             p.push_back(u[k].num);
            k++;
         }
     }
    if(p.size()==0) {
        printf("1\n");
        return 0;
    }
     n=p.size();
    dp[1] = p[0] ;   dp[0] = -inf ;
    for( i = ans = 1 ; i < n ; i++)
    {
        int l = 0 , r = ans ;
        while( l <= r )
        {
           int  mid = ( l + r ) >> 1 ;
            if( dp[mid] >= p[i] ) r = mid - 1 ;
            else l = mid + 1 ;
        }
        if( r == ans ) ans++,dp[r+1] = p[i] ;
        else if( dp[r+1] > p[i] ) dp[r+1] = p[i] ;
    }
    printf("%d\n",ans+1);
    return 0;
}

 

最长公共子序列问题:

 

给定2个字符串,求其最长公共子串。如abcde和dbada的最长公共字串为bd。

动态规划:dp[i][j]表示A串前i个和B串前j个的最长公共子串的长度。

若A[i] == B[j] , dp[i][j] = dp[i-1][j-1] + 1;

否则 dp[i][j] = max(dp[i-1][j],dp[i][j-1]);

时间复杂度O(N*M)。

dp[i][j]仅在A[i]==B[j]处才增加,对于不相等的地方对最终值是没有影响的。

故枚举相等点处可以对其进行优化。

则对于dp[i][j](这里只计算A[i]==B[j]的i和j),取最大的dp[p][q],满足(p<i,q<j),通过二叉搜索树可以再logn的时间里获取到最大的dp[p][q],区间在[0,j)。

这里也可将其转化为最长递增子序列问题。

举例说明:

A:abdba

B:dbaaba

则1:先顺序扫描A串,取其在B串的所有位置:

    2:a(2,3,5) b(1,4) d(0)。

    3:用每个字母的反序列替换,则最终的最长严格递增子序列的长度即为解。

替换结果:532 41 0 41 532

最大长度为3.

简单说明:上面的序列和最长公共子串是等价的。

对于一个满足最长严格递增子序列的序列,该序列必对应一个匹配的子串。

反序是为了在递增子串中,每个字母对应的序列最多只有一个被选出。

反证法可知不存在更大的公共子串,因为如果存在,则求得的最长递增子序列不是最长的,矛盾。

最长递增子序列可在O(NLogN)的时间内算出。

dp[i] = max(dp[j]+1) ( 满足 a[i] > a[j] && i > j )

显然对于同样的如dp[k] = 3,假定k有多个,记为看k1,k2,.....,km 设k1 < k2 < .... < km

在计算dp[i]的时候,k2,k3,....,km显然对结果没有帮助,取当前最小的k,

满足ans[k] = p (最小的p使得dp[p]=k) ,每次二分,更新ans[dp[i]] = min(ans[dp[i]],i).

 

ps:LCS在最终的时间复杂度上不是严格的O(nlogn),不知均摊上是不是。

举个退化的例子:

如A:aaa

    B:aaaa

则序列321032103210

长度变成了n*m ,最终时间复杂度O(n*m*(lognm)) > O(n*m)。

这种情况不知有没有很好的解决办法。

最长公共子序列 nlogn

标签:

原文地址:http://www.cnblogs.com/zxhl/p/5451403.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!