最长子序列问题:从中找出最长的字符序列,比如: cnblogs和belong。这两个字符串的最长子序列就是blog。

动态规划:通过分解大问题,不断的将大问题变成小问题,最终整合所有解,得出最优解(和递归有点相似,但是递归的时间复杂度太过大,通过动态规划的解决,可以将一部分的时间复杂度调整成空间复杂度)

Xm = {x1,x2,x3...xm},Yn = {y1,y2,y3,...yn},求X和Y的最长子序列。

1,假设Z = {z1,z2,..., zk}是X和Y的最长子序列,那么可以看出(解1)

  • 如果xm = yn ,那么Zk-1 就是Xm-1和Yn-1的LCS(因为最后一个元素相等且已经规定Zk是Xm和Yn的LCS,所以Zk-1 自然就是Xm-1和Yn-1的LCS)
  • 如果xm ≠ yn ,那么有Zk = {Xm-1,Yn}的LCS或者Zk = {Xm,Yn-1}的LCS(因为X和Y的最后一个元素不相同,所以自然最后一个元素不在LCS序列中,但是并不知道到底是哪个字符串不存在于序列中,所以这里拆分成了两个子问题)

所以通过上面的分析,可以得出状态转移方程(该方程记录的是所有状态改变的过程,即记录每个状态的过程,通过二维数组记录)

c[i,j] =   1.  0                   i=0 || j =0

   2.  c[i-1,j-1] +1             i > 0 and j > 0 and xi = yj

     3.  Math.Max(c[i-1,j],c[i,j-1])        i > 0 and j > 0 and xi ≠ yj

(解释2:即始终保存的是目前最长的子串长度,通过解1的第一点可以看出如果最后一个元素相同那么LCS就是两个字符串长度-1的LCS,由于原问题比较庞大,所以现在是通过拆分原问题将它变成很多小问题来解决;解释3:同参考解释2和解1的第二点)

状态转移表如下表显示:

      i            
      1 2 3 4 5 6 7
      c n b l o g s
j 1 b 0 0 1 1 1 1 1
  2 e 0 0 1 1 1 1 1
  3 l 0 0 1 2 2 2 2
  4 o 0 0 1 2 3 3 3
  5 n 0 1 1 2 3 3 3
  6 g 0 1 1 2 3 4 4

代码如下:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApp1
{
class Program
{
public static int[,] flag = new int[,];
public static string str1 = "cnblogs";
public static string str2 = "belong";
static void Main(string[] args)
{ int[,] c = Lcs(str1, str2);
for (int i = ;i<str1.Length+;++i)
{
for(int j= ;j<str2.Length+;++j)
{
Console.Write(c[i, j]);
}
Console.Write("\n");
}
Console.WriteLine("输出LCS序列");
printLcs(str1.Length,str2.Length);
Console.ReadKey();
} public static void printLcs(int i,int j)
{
if (i == || j == ) return;
if (flag[i, j] == )
{
//Console.Write(str1[i - 1]);
printLcs(i - , j - );
Console.Write(str1[i - ]); }
else if (flag[i, j] == )
{
printLcs(i - , j); }
else
printLcs(i, j - );
} public static int[,] Lcs(string str1, string str2)
{
int len1 = str1.Length+;
int len2 = str2.Length+;
int[,] c = new int[len1, len2];
string[] sc = new string[Math.Max(len1, len2)];
for(int i = ;i<len1;i++)
{
for(int j =;j<len2;j++)
{
if (i == || j == )
c[i, j] = ;
else if (str1[i - ] == str2[j - ])
{
c[i, j] = c[i - , j - ] + ;
flag[i, j] = ;
}
else
{
c[i, j] = Math.Max(c[i, j - ], c[i - , j]);
if (c[i, j - ] <= c[i - , j])
flag[i, j] = ;
else
flag[i, j] = -;
}
}
}
for(int i = ;i<;++i)
{
for(int j=;j<;++j)
{
Console.Write(flag[i, j] + " ");
}
Console.Write("\n");
}
return c;
}
}
}

其中,完成状态转移的片段如下

 int len1 = str1.Length+;
int len2 = str2.Length+;
int[,] c = new int[len1, len2];
string[] sc = new string[Math.Max(len1, len2)];
for(int i = ;i<len1;i++)
{
for(int j =;j<len2;j++)
{
if (i == || j == )
c[i, j] = ;
else if (str1[i - ] == str2[j - ])
{
c[i, j] = c[i - , j - ] + ;
flag[i, j] = ;
}
else
{
c[i, j] = Math.Max(c[i, j - ], c[i - , j]);
if (c[i, j - ] <= c[i - , j])
flag[i, j] = ;
else
flag[i, j] = -;
}
}
}

注:该片段代码只提供状态转移的过程和该问题的最长子序列的长度,若需要确定LCS的元素,则需要通过另外一个数组保存状态转移的信息(即该状态是从何而来,是从哪个数据继承过来的);flag数组代表的就是状态转移的信息,这里分成三种情况,代表着当前状态的来源,分别是:c[i-1,j-1]、c[i-1,j]、c[i,j-1]。

实现代码如下:

  public static void printLcs(int i,int j)
{
if (i == || j == ) return;
if (flag[i, j] == )
{
//Console.Write(str1[i - 1]);
printLcs(i - , j - );
Console.Write(str1[i - ]); }
else if (flag[i, j] == )
{
printLcs(i - , j); }
else
printLcs(i, j - );
}

该问题通过保存状态转移的信息,然后再利用递归的方法得出结果。

代码解读:第一个  if 中,判断传入的 i 与 j  (i 和 j  代表存储状态转移信息的数组的下标),倘若所有数据已经遍历完毕,那么终止递归;第二个 if 中,如果flag中保存的数据 == 0 则表示当前数据来自于 c[i-1,j-1] 所以此时递归参数就是 i-1 和 j-1,而 else if 和 else 也相应的代表另外两个来源方向。

注:由于该方法是从后到前的递归,所以Console要在函数后面,意味着先递归再输出,那么输出的结果就是从头开始,加入先输出再递归的话,那么输出的结果就是刚好相反的,比如正确的结果是 blog,那么由于错误的输出方式则造成了现在的结果是 golb。

为什么选择从后往前递归呢?第一点当然是为了方便,大家都习惯于将递归的结束条件写成与0有关,第二点是因为该数据状态的继承是从前面的数据得到的,而不是从后面的数据得到的,所以只有后面的数据可以找到前面的数据,而前面的无法预知后面的。(我猜是这样的!)

最后一点:动态规划中最重要的一步是写出该问题的状态转移方程,将问题划分成若干子问题;最核心的一步是知道要用动态规划(这不是废话吗!通常碰到类似于递归,分治的,如果不可行或者时间复杂度过于庞大的话就要考虑动态规划了)

动态规划(1)——最长子序列(LCS)问题的更多相关文章

  1. Poj1159 Palindrome(动态规划DP求最大公共子序列LCS)

    一.Description A palindrome is a symmetrical string, that is, a string read identically from left to ...

  2. 算法:Common Subsequence(动态规划 Java 最长子序列)

    Description A subsequence of a given sequence is the given sequence with some elements (possible non ...

  3. hdu1422重温世界杯(动态规划,最长子序列)

    重温世界杯 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  4. nyoj17-单调递增最长子序列-(dp)

    17-单调递增最长子序列 内存限制:64MB 时间限制:3000ms 特判: No通过数:125 提交数:259 难度:4 题目描述: 求一个字符串的最长递增子序列的长度如:dabdbf最长递增子序列 ...

  5. 【LCS,LIS】最长公共子序列、单调递增最长子序列

    单调递增最长子序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 求一个字符串的最长递增子序列的长度如:dabdbf最长递增子序列就是abdf,长度为4   输入 ...

  6. 动态规划:ZOJ1074-最大和子矩阵 DP(最长子序列的升级版)

    To the Max Time Limit:1 Second     Memory Limit:32768 KB Problem Given a two-dimensional array of po ...

  7. nyoj 17-单调递增最长子序列 && poj 2533(动态规划,演算法)

    17-单调递增最长子序列 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:21 submit:49 题目描述: 求一个字符串的最长递增子序列的长度 如 ...

  8. 动态规划 最长公共子序列 LCS,最长单独递增子序列,最长公共子串

    LCS:给出两个序列S1和S2,求出的这两个序列的最大公共部分S3就是就是S1和S2的最长公共子序列了.公共部分 必须是以相同的顺序出现,但是不必要是连续的. 选出最长公共子序列.对于长度为n的序列, ...

  9. 最长递增子序列问题 nyoj 17单调递增最长子序列 nyoj 79拦截导弹

    一,    最长递增子序列问题的描述 设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1< ...

随机推荐

  1. 最小二乘法 good

    最小二乘法也称为最小平方法,是一种数据优化技术,它通过最小化误差的平方和寻找数据的最佳函数匹配. 最小二乘法最初由高尔顿在创立回归分析的时候提出,现在已经成为探索变量间关系最重要的方法,最小二乘法根据 ...

  2. Hadoop 3、Hadoop 分布式存储系统 HDFS(好多彩色图)

    HDFS是Hadoop Distribute File System 的简称,也就是Hadoop的一个分布式文件系统. 一.HDFS的优缺点 1.HDFS优点: a.高容错性 .数据保存多个副本 .数 ...

  3. Google+团队如何测试移动应用 - from Google Testing Blog

    How the Google+ Team Tests Mobile Apps by Eduardo Bravo Ortiz “移动第一”在当下已成为很多公司的口头禅.但是能够用一种合理的方法来测试移动 ...

  4. 95+强悍的jQuery图形效果插件

    现在的网站越来越离不开图形,好的图像效果能让你的网站增色不少.通过JQuery图形效果插件可以很容易的给你的网站添加一些很酷的效果. 使用JQuery插件其实比想象的要容易很多,效果也超乎想象.在本文 ...

  5. XML转义字符 如"&"

    解析数据 XML 解析器通常情况下会处理XML文档中的所有文本. 当XML元素被解析的时候,XML元素内部的文本也会被解析,例如: <message>Hello Word!</mes ...

  6. 前端 JS 修炼(第一天)包装对象、作用域、创建对象

    1.js基本概念以及注意 直接量 :程序中直接使用的数据值.下面列出的都是直接量: 1 12 //数字 2 1.2 //小数 3 "hello world" //字符串文本 4 t ...

  7. Java 函数传入参数后,究竟发生了什么?java函数传参数原理解析

    JAVA函数在传入参数A时,会在函数作用周期内生成一个与参数相同类型的局部变量B. B与A指向同一块内存区域,并且具有相同的名字如param. 在函数内所有对param的操作都是对B的操作.对B进行赋 ...

  8. oracle11g安装时出现程序未找到文件解决办法

    在安装的最后可能会出现如下问题 解决办法如下 将win64_11gR2_database_2of2中的\win64_11gR2_database_2of2\database\stage\Compone ...

  9. shell多线程(2)之基于管道实现并发

    在shell脚本里批量执行程序是比较常见的方式,如果程序很多,每个执行时间比较长,则顺序执行需要花费大量的时间. 此时并发就成为我们考虑的方向. 上篇<shell多线程>中我们已经简单实现 ...

  10. 使用Core Audio实现VoIP通用音频模块

    最近一直在做iOS音频技术相关的项目,由于单项直播SDK,互动直播SDK(iOS/Mac),短视频SDK,都会用到音频技术,因此在这里收集三个SDK的音频技术需求,开发一个通用的音频模块用于三个SDK ...