zoukankan      html  css  js  c++  java
  • luoguP3402 最长公共子序列(LCS-->LIS)

    题目背景

    DJL为了避免成为一只咸鱼,来找Johann学习怎么求最长公共子序列。

    题目描述

    经过长时间的摸索和练习,DJL终于学会了怎么求LCS。Johann感觉DJL孺子可教,就给他布置了一个课后作业:

    给定两个长度分别为n和m的序列,序列中的每个元素都是正整数。保证每个序列中的各个元素互不相同。求这两个序列的最长公共子序列的长度。

    DJL最讨厌重复劳动,所以不想做那些做过的题。于是他找你来帮他做作业。

    输入输出格式

    输入格式:
    第一行两个整数n和m,表示两个数列的长度。
    第二行一行n个整数a_1,a_2,…,a_n,保证1≤a_i≤〖10〗^9。
    第三行一行m个整数b_1,b_2,…,b_m,保证1≤b_i≤〖10〗^9。

    输出格式:
    一行一个整数,表示两个数列的最长公共子序列的长度。

    输入输出样例

    输入样例#1:
    6 6
    1 3 5 7 9 8
    3 4 5 6 7 8

    输出样例#1:
    4

    分析:
    一眼秒N^2dp
    然而面对300000就jj了,
    这样我们就要学习新知识了:

    最长公共子序列问题(LCS):

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

    显然dp啊:

    一、n^2算法

    f[i][j]表示a串前i个和b串前j个的最长公共子串的长度
    若A[i]==B[j],f[i][j]=f[i-1][j-1]+1
    否则f[i][j]=max(f[i-1][j],f[i][j-1])

    二、nlogn算法

    f[i][j]仅在A[i]==B[j]处才增加,
    对于不相等的地方对最终值是没有影响的
    故枚举相等点处可以对其进行优化
    则对于f[i][j](这里只计算a[i]==b[j]的i和j),
    取最大的f[p][q],满足(p < i,q < j),通过二叉搜索树可以在logn的时间里获取到最大的f[p][q])。

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

    举例说明:
    A:abdba
    B:dbaaba

    1:先顺序扫描A串,取其在B串的所有位置:
    2:a(2,3,5) b(1,4) d(0)
    3:A用每个字母的反序列替换,则最终的最长严格递增子序列的长度即为解
    最长递增子序列可在O(nlogn)的时间内算出

    替换结果:532 41 0 41 532
    对于序列53241041532做LIS

    最大长度为3.

    LIS(最长上升子序列nlogn)

    建一个g数组,g[i]表示长度为i的lis中,作为结尾的最小的数,
    这样的g是单调增的,
    每次考虑以i为结尾的上升子序列时,我们只要在g二分中找到小于a[i]的最大值g[j]
    f[i]=j+1

    然而在真正写的时候f数组就可以省去了

        g[1]=num[1];  //长度为1的最小 
        //f[1]=1;  写完会发现f可有可无 
        int t=1;  //当前最长上升子序列的长度 
        for (i=2;i<=n;i++)
        {
            int l=1,r=t,mid;
            while (l<=r)
            {
                mid=(l+r)>>1;
                if (g[mid]<num[i])  //找到小于a[i]的最大的一个 
                   l=mid+1;  //mid+1  不知不觉就+1 
                else r=mid-1;
            }
            g[l]=num[i]; //l就是长度 
            //f[i]=l;
            if (l>t) t=l;
        }
        return t;  //最长上升子序列长度

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

    对于一个满足最长严格递增子序列的序列,该序列必对应一个匹配的子串
    a串中的每一个数子都是该字符在b中出现的位置
    反序是为了在递增子串中,每个字母对应的序列最多只有一个被选出
    反证法可知不存在更大的公共子串,因为如果存在,则求得的最长递增子序列不是最长的,矛盾。
    不太明白。。。

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

    举个退化的例子:
    A:aaa
    B:aaaa

    则序列=>321032103210
    长度变成了n*m ,最终时间复杂度O(n*m*(lognm)) > O(n*m)。
    这种情况不知有没有很好的解决办法

    然而

    这道题保证每个序列中的各个元素互不相同
    这样就可以把A转化成一个n级别的序列,不然的话时间会退化得很厉害
    又因为ai<=1e9,这样就需要一个map映射一下,记录每个数对应的位置,当然还有hash做法

    //map->
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<map>
    
    using namespace std;
    
    const int N=300005;
    int n,m,a[N],g[N],num[N];
    map<int,int> mp;
    
    /*void doit()    //n^2
    {
        int i,j,ans=0;
        for (i=1;i<=n;i++)
            for (j=1;j<=m;j++)
            {
                if (a[i]==b[j]) f[i][j]=f[i-1][j-1]+1;
                else f[i][j]=max(f[i-1][j],f[i][j-1]);
            }
        printf("%d",f[n][m]);
    }*/
    
    int LCS()
    {
        int i,j;
        int s=1;
        while (!num[s]) num[s]=999999,s++;  //如果a[s]==0表示b中没有这个数 
        g[1]=num[1];  //长度为1的最小 
        //f[1]=1;  写完会发现f可有可无 
        int t=1;  //当前最长上升子序列的长度 
        for (i=2;i<=n;i++)
        {
            int l=1,r=t,mid;
            while (l<=r)
            {
                mid=(l+r)>>1;
                if (g[mid]<num[i])  //找到小于a[i]的最大的一个 
                   l=mid+1;  //mid+1  不知不觉就+1 
                else r=mid-1;
            }
            g[l]=num[i]; //l就是长度 
            //f[i]=l;
            if (l>t) t=l;
        }
        return t;
    }
    
    void doit()
    {
        int i,j,cnt=0;
        for (i=1;i<=n;i++) num[i]=mp[a[i]];  //a中的数字在b中的位置 
        printf("%d",LCS());
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        for (int i=1;i<=m;i++) 
        {
            int u;
            scanf("%d",&u);
            mp[u]=i;  //数字u在b中出现的位置 
        }
        doit();
        return 0;
    }
  • 相关阅读:
    linux网络管理之netstat命令
    linuxping命令
    linux网络管理-ip
    linux网络管理--网络配置及命令
    汇编基础整理
    windows编辑文本和unix编辑文本的回车符问题
    Zend Studio 8.0.1 新建远程项目无法展示远程项目列表的问题
    HTTPS 服务器搭建
    mysql中的JOIN用法总结
    tcpdump使用和TCP/IP包分析
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673477.html
Copyright © 2011-2022 走看看