zoukankan      html  css  js  c++  java
  • 最长公共子序列 nlogn

    先来个板子

    #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
    ");
            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
    ",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)。

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

  • 相关阅读:
    Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task(枚举/最大连续子序列)
    Educational Codeforces Round 88 (Rated for Div. 2) A. Berland Poker(数学)
    Educational Codeforces Round 88 (Rated for Div. 2) E. Modular Stability(数论)
    Educational Codeforces Round 88 (Rated for Div. 2) C. Mixing Water(数学/二分)
    Codeforces Round #644 (Div. 3)
    Educational Codeforces Round 76 (Rated for Div. 2)
    Educational Codeforces Round 77 (Rated for Div. 2)
    Educational Codeforces Round 87 (Rated for Div. 2)
    AtCoder Beginner Contest 168
    Codeforces Round #643 (Div. 2)
  • 原文地址:https://www.cnblogs.com/zxhl/p/5451403.html
Copyright © 2011-2022 走看看