LCS和ED

原创 2018年04月15日 01:32:15

LCS和ED


  LCS(最长公共子序列)和ED(edit distance)是动态规划的两个经典模型,因为要用到二维的dp数组,所以我在初学时理解起来很困难,为加深理解和防止遗忘特地写成博客防止遗忘。两者性质相似,写在一起方便整理。两个都能计算数组和字符串,这里为方便书写,都写成数组。

LCS

  LCS(最长公共子序列)就是字面意思,即两个串中完全相同的部分。
状态转移方程:
  dp[i][j]=max{dp[i-1][j-1]+1(s[i]=t[j]),dp[i-1][j],dp[i][j-1]}
s和t两个串中,分别以s[i]和t[j]结尾的子串的LCS的长度为dp[i][j],其长度分为以下几种情况:

  1. s[i]=t[j]
      若两个子串末尾相同,则dp[i][j]就等于以s[i-1]和t[j-1]为结尾的子串的LCS长度加1,即dp[i][j]=dp[i-1][j-1]+1;
  2. s[i]!=t[j]
      若两个子串末尾不同,则dp[i][j]就等于以s[i]和t[j-1]为结尾的子串的LCS和以s[i-1]和t[j]为结尾的子串的LCS的长度的最大值。

  我在初学时很难理解——为什么只通过判断子串结尾就能确定两子串的LCS呢?但仔细研究就会发现,在算dp[i][j]的过程中,因为从1开始遍历,dp[i][j]经过不断的累加,实际调用的dp[i-1][j-1]之类的的时候相当于s[i]和t[j]之前的所有都已经变成完全相同的东西,比较大小是否相同只是为了确认现有的LCS会变得更长(s[i]=t[j]),还是不变化(s[i]!=t[j])。理解了这一点就简单多了。

代码:

#include<bits/stdc++.h>
using namespace std;
int t[105],s[105],dp[105][105],n,m;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&s[i]);
    for(int i=1;i<=m;i++)scanf("%d",&t[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(s[i]==t[j])dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],d[i][j-1]);
        }
    printf("%d\n",dp[n][m]);
    return 0;
}

ED

  ED(edit distance)意思是将一个串变为另一个串所需的最小修改次数。其大致思路和LCS一致,都是比较子串末尾来得到以s[i]和t[j]为结尾的子串变成完全相同所需的最小操作次数。操作内容包括删除添加和修改。

状态转移方程:

(2)dp[i][j]={dp[i1][j]+1s[i],dp[i1][j1],s[i]=t[j]dp[i][j1]+1t[j],dp[i1][j1]+1t[j]

  1. s[i]=t[j]
      末尾相同就不需要修改,修改次数就等于dp[i-1][j-1]。

  2. s[i]!=t[j]
      末尾不同就能分为3类,删掉一个、替换一个、和插入一个。不需要过多说明。

  本题需要特殊处理的就是若有一个串的长度为0时,需要自己手动将dp[0][j]和dp[i][0]的大小手动定义为j和i。其余的不管是思路还是做法都和LCS大致相似。

代码:

#include<bits/stdc++.h>
using namespace std;
int t[105],s[105],dp[105][105],n,m;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&s[i]);
        dp[i][0]=i;
    }
    for(int i=1;i<=m;i++){
        scanf("%d",&t[i]);
        dp[0][i]=i;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(s[i]==t[j])dp[i][j]=dp[i-1][j-1];
            else dp[i][j]=min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
        }
    printf("%d\n",dp[n][m]);
    return 0;
}

  总之,LCS和ED作为两个经典模型,对类似的问题都有很大的启发作用,其思路值得深刻的理解,对今后dp的学习会有很大帮助。

java反射和注解开发(备java基础,javaee框架原理)

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 qq群:362969068
  • 2016年10月18日 09:40

LCS的两种解法比较

动态规划问题一般具有两个要素:最优子结构与子问题重叠。 通常在求解LCS问题时,我们都会用到两种方法: 1. momo-ization(备忘录方法) 利用了该问题的重叠子问题特性,而重叠子问题可...
  • u014449866
  • u014449866
  • 2015-04-20 16:36:47
  • 572

动态规划求解最长公共子序列(LCS)

看了《算法导论》中文第二版P208的动态规划求解LCS问题,觉得很赞,但总觉得算导写得有些晦涩,希望自己能写得简单易懂一些,纯当锻炼了,欢迎指导交流。 首先,子序列和子串是不一样的。子串是连续的,而子...
  • zhouworld16
  • zhouworld16
  • 2013-11-15 20:23:47
  • 6821

LCS问题求解-动态规划

1.何为LCS问题: 在求解LCS问题之前,我们需要先了解一下什么叫做最长公共子序列 最长公共子序列:用我们最容易通俗理解的话语来解释的话,最长公共子序列就是两个或者多个串中,最长的相同的子序列 ...
  • ltyqljhwcm
  • ltyqljhwcm
  • 2016-09-15 00:11:54
  • 2516

LCS算法的两种JAVA实现方式

给定字符串A,B Solution  I: 1.构造数组 c i  j 描述A串的前i位和B串的前J位的LCS长度 2.构造数组 trace  i  j  描述max相应位置得到的长度是由哪一步得出的...
  • Lyz1052
  • Lyz1052
  • 2015-06-14 12:39:30
  • 889

LCS问题,JAVA实现

本文参照July的博客,第十一章,只给出个人理解和java实现
  • kringpin_lin
  • kringpin_lin
  • 2014-06-25 20:49:00
  • 1095

LCS算法的C++实现

最长公共子序列LCS的C++实现
  • zmq570235977
  • zmq570235977
  • 2015-11-15 15:02:40
  • 1824

LCS详解

动态规划算法解LCS问题   作者 July  二零一零年十二月三十一日 本文参考:微软面试100题系列V0.1版第19、56题、算法导论、维基百科。 第一部分、什么是...
  • sumword_
  • sumword_
  • 2016-10-18 22:57:11
  • 1336

LCS 及LCS+滚动数组 模板

LCS 模板: #include #include #include using namespace std; char a[1010],b[1010]; int dp[1010][101...
  • q923714892
  • q923714892
  • 2016-08-11 20:27:19
  • 387

最长公共子序列(LCS)算法实验

  • 2012年04月19日 21:48
  • 28KB
  • 下载
收藏助手
不良信息举报
您举报文章:LCS和ED
举报原因:
原因补充:

(最多只允许输入30个字)