转载注明出处:http://blog.csdn.net/wdq347/article/details/9001005 (修正了一些错误,并自己重写了代码)


最长公共子序列(LCS)最常见的算法是时间复杂度为O(n^2)的动态规划(DP)算法,但在James W. Hunt和Thomas G. Szymansky 的论文"A Fast Algorithm for Computing Longest Common Subsequence"中,给出了O(nlogn)下限的一种算法。

定理:设序列A长度为n,{A(i)},序列B长度为m,{B(i)},考虑A中所有元素在B中的序号,即A某元素在B的序号为{Pk1,Pk2,..},将这些序号按照降序排列,然后按照A中的顺序得到一个新序列,此新序列的最长严格递增子序列即对应为A、B的最长公共子序列。

举例来说,A={a,b,c,d,b},B={b,c,a,b},则a对应在B的序号为2,b对应序号为{3,0},c对应序号为1,d对应为空集,生成的新序列为{2, 3, 0, 1, 3, 0},其最长严格递增子序列为{0,1,3},对应的公共子序列为{b, c, b}

原论文的证明过程较复杂,其实可以简单的通过一一对应来证明。即证明A、B的一个公共子序列和新序列的一个严格递增子序列一一对应。

(1) A、B的一个公共子序列对应新序列的一个严格递增子序列

假设A、B的某一个公共子序列长度为k,则其公共子序列在A和B中可以写为

{Ai1,Ai2, ..., Aik}

{Bj1,Bj2, ..., Bjk}

如此有Ai1 = Aj1,Ai2 = Aj2, ...., Aik = Ajk, 考虑元素Bj1在B中的序号P(Bj1),则有

P(Bj1)< P(Bj2) < ... < P(Bjk)

注意此严格递增子序列属于新序列的一个子序列,因此得证

(2) 新序列的一个严格递增子序列对应A、B的一个公共子序列

设新序列的一个严格递增子序列{P1,P2, ..., Pk},任意两个相同的P不可能属于A中同一个元素,因为A中某元素在B中的序号按照降序排列,但此序列为严格递增序列,矛盾。所以每个P均对应于A中不同位置的元素,设为{Ai1, Ai2, ..., Aik}。

因为P是严格递增序列,则每个P也对应B中唯一的一个元素,假设为{Bj1,Bj2, ..., Bjk},由P的定义可知Ai1= Bj1, Ai2 = Bj2, ...., Aik = Bjk,因此得证。

实现上比较复杂,有以下几个步骤:

(1) 对序列B排序

(2) 计算A中每个元素在B中的序号,并构成新序列

(3) 使用LIS的方法计算最长严格递增子序列

(4) 获取最长公共子序列

性能分析:

(1) 排序复杂度为nlogn

(2) 获取一个元素在B中的序号的复杂度,最小为logn,最大为n,获取所有元素的复杂度为 nlogn === n*n

(3) LIS 复杂度为nlogn

因此总体复杂度在nlogn 到 n*n logn之间,但如果(2)
步骤中A中元素在B中的序号对数很少时,性能相当优越,在实际测试时,string
中均为小写字母,长度为10000的情况下,这种方法比普通的LCS快一倍以上;如果string
中的字符扩展成char,即0-255,则这种方法比普通的LCS快至少一个数量级。


以下是参考代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#include<string>
#include<sstream>
#define eps 1e-9
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define MAXN 1005
#define MAXM 40005
#define INF 0x3fffffff
using namespace std;
typedef long long LL; struct node
{
char c;
int num;
} u[]; int i,j,k,n,m,x,y,T,ans,big,cas,num,len;
bool flag; bool cmp(node a,node b)
{
if (a.c==b.c) return a.num>b.num;
return a.c<b.c;
} vector <int> p;
char a[],b[],c[];
int lena,lenb,dp[]; int main()
{
scanf("%s",a);//读入a串
scanf("%s",b);//读入b串 lena=strlen(a);
lenb=strlen(b); for (i=;i<lenb;i++)
{
u[i].c=b[i];
u[i].num=i;
} sort(u,u+lenb,cmp);//对b串排序 for (i=;i<lenb;i++)//排序后存入字符串c中,便于使用lower_bound
{
c[i]=u[i].c;
}
c[lenb]='\0'; for (i=;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++;
}
} n=p.size(); memset(dp,,sizeof(dp));//计算最长上升子序列
num=;
for (i=;i<n;i++)
{
if (p[i]>dp[num])
{
dp[++num]=p[i]; }else
{
k=lower_bound(dp+,dp++num,p[i])-dp;
dp[k]=p[i]; }
} printf("%d\n",num); return ;
}

【算法】最长公共子序列(nlogn)的更多相关文章

  1. 最长公共子序列 nlogn

    先来个板子 #include<bits/stdc++.h> using namespace std; , M = 1e6+, mod = 1e9+, inf = 1e9+; typedef ...

  2. 编程算法 - 最长公共子序列(LCS) 代码(C)

    最长公共子序列(LCS) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符 ...

  3. 动态规划算法——最长公共子序列问题(java实现)

    已知序列X=(A,B,C,A,B,D,A)和序列Y=(B,A,D,B,A),求它们的最长公共子序列S. /* * LCSLength.java * Version 1.0.0 * Created on ...

  4. 经典算法-最长公共子序列(LCS)与最长公共子串(DP)

    public static int lcs(String str1, String str2) { int len1 = str1.length(); int len2 = str2.length() ...

  5. 动态规划经典算法--最长公共子序列 LCS

    转移方程 代码: //法一: #include <bits/stdc++.h> using namespace std; //---------------https://lunatic. ...

  6. LCS问题(最长公共子序列)-动态规划实现

    问题描述: 问题] 求两字符序列的最长公共字符子序列 注意: 并不要求子串(字符串一)的字符必须连续出现在字符串二中. 思路分析: 最优子结构和重叠子问题的性质都具有,所以要采取动态规划的算法 最长公 ...

  7. 最长公共子序列&最长公共子串

    首先区别最长公共子串和最长公共子序列  LCS(计算机科学算法:最长公共子序列)_百度百科 最长公共子串,这个子串要求在原字符串中是连续的.而最长公共子序列则并不要求连续. 最长公共子序列: http ...

  8. 算法设计 - LCS 最长公共子序列&&最长公共子串 &&LIS 最长递增子序列

    出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的 ...

  9. P3402 最长公共子序列(nlogn)

    P3402 最长公共子序列 题目背景 DJL为了避免成为一只咸鱼,来找Johann学习怎么求最长公共子序列. 题目描述 经过长时间的摸索和练习,DJL终于学会了怎么求LCS.Johann感觉DJL孺子 ...

随机推荐

  1. phpcms get标签说明

    {pc:get sql="SELECT * FROM phpcms_member" cache="3600" page="$page" db ...

  2. CSS浮动元素的水平居中

    方法一: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> ...

  3. 一次性安装src.rpm编译所依赖的软件包

    yum-builddep SRPMS/fcitx-4.2.8.4-4.1.cgdl21.src.rpm NAME       yum-builddep - install missing depend ...

  4. 定制linux中的Gtk theme<一>如何设置窗口按钮的多态效果

    GTK主题之个人理解: GTK 主题引擎(包含代码所需的图形元素) +  主题配置文件(gtkrc文件)+ 数据资源文件(如图片等)    三者所呈现给用户的视觉风格效果 GTK拥有一套大量的widg ...

  5. PHPStorm自动提示方法

    第一种: /** * 一定要写@return static * @return static */ public static function getInstance() { $className ...

  6. oracle 查询dblink的方法

    oracle 查询dblink的方法: SYSDBA登录, sys登录 SELECT * FROM SYS.link$;

  7. 【HDOJ】2266 How Many Equations Can You Find

    简单DFS. #include <cstdio> #include <cstring> #define MAXN 15 char str[MAXN]; __int64 x; i ...

  8. swiper 多个循环的实现

    swiper 最好要一一对应,最好与id关联. new Swiper('#guess .swiper-container', { pagination: '#guess .swiper-paginat ...

  9. 2015第37周五javascript函数arguments对象巧用一

    Javascript函数的一个巧妙利用:假定action中有一个JSONObject类型的对象data,其值有可能为空,则前台JSP页面的JS代码中想直接通过EL表达式,即${data}的形式访问对象 ...

  10. 矩阵(快速幂):COGS 963. [NOI2012] 随机数生成器

    963. [NOI2012] 随机数生成器 ★★   输入文件:randoma.in   输出文件:randoma.out   简单对比 时间限制:1 s   内存限制:128 MB [问题描述] 栋 ...