ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))
这道题被51Nod定为基础题(这要求有点高啊),我感觉应该可以算作一级或者二级题目,主要原因不是动态规划的状态转移方程的问题,而是需要理解最后的回溯算法。
题目大意:找到两个字符串中最长的子序列,子序列的要求满足其中字符的顺序和字母在两个序列中都必须相同,任意输出一个符合题意的子序列
首先是最基本的最长公共子序列的状态转移问题:
这里的maxLen[i][j]数组的意思就是保存s1的前 i 个字符和s2的前 j 个字符匹配的状态。
举个例子:maxLen[3][6]即表明在s1的前3个字符和s2的前6个字符中匹配到的最长公共子序列的长度。
如果能够理解这里,那么动态规划的“无后效性”这一关键性质也就理解了,动态规划的关键就在于找到一个合理的最优状态使得其在后续的状态转移中不会被影响,而其状态就是题设最优问题的子优化问题,这样的状态就得以让我们逐步向父优化问题递进。
基于此可以将maxLen[i-1][j-1]作为maxLen[i][j]的子问题
当s1的第 i 字符和s2的第 j 字符匹配时有状态转移方程:maxLen[i][j] = maxLen[i-1][j-1] + 1
而maxLen[i-1][j]和maxLen[i][j-1]则是在i和j不匹配时将前一个状态的最优解传递给下一状态
即不匹配时有状态转移方程:maxLen[i][j] = max(maxLen[i-1][j], maxLen[i][]j-1)
以下给出基于此状态转移方程的计数代码
//两字符串中找出一个最长字符串
//ps:其字符在两字符串中存在且顺序相同-记录字符个数 #define max(x,y) ((x)>(y)?(x):(y)) int maxlen[MAX][MAX]; //s1前i个字符和s2前i个字符最长匹配 int Matching(char s1[],char s2[])
{
memset(maxlen, , sizeof(maxlen));
int len1 = strlen(s1);
int len2 = strlen(s2);
for (i = ; i <= len1; i++)
for (j = ; j <= len2; j++)
if (s1[i - ] == s2[j - ]) //s1前i与s2前j字符串尾字符匹配
maxlen[i][j] = maxlen[i - ][j - ] + ;
else //不匹配
maxlen[i][j] = max(maxlen[i][j - ], maxlen[i - ][j]);
return maxlen[len1][len2];
}e
接着是该题最重要的回溯算法,这一步是输出子序列的关键:
借用一张图描述该算法思路如下:

要从标明了各状态的二维数组中找出子序列的方法可以作如下描述:
当父状态maxLen[i][j]和最相近的两个子状态maxLen[i-1][j]和maxLen[i][j-1]都不相同的时候
说明此时maxLen[i][j]做了该运算:maxLen[i][j] = maxLen[i-1][j-1] + 1
当父状态maxLen[i][j]和最相近的两个子状态中任一个相同的时候
说明此时maxLen[i][j]做了该运算:maxLen[i][j] = max(maxLen[i-1][j], maxLen[i][]j-1)
需要注意的是在父状态和最近子状态都相同时将衍生平移的两个方向,由此可以得出所有最长公共子序列的集合。
由于该题只需要输出任一个,因此指定一个方向回溯即可。
因此合并上述的两个算法可以得出该题的最终算法:
//求出最长公共子序列并输出任一子序列
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define MAX 1001
#define max(x,y) ((x)>(y)?(x):(y)) char s1[MAX], s2[MAX];
int maxLen[MAX][MAX];
char ans[MAX]; int main()
{
int k = ;
scanf("%s%s", s1,s2);
int len1 = strlen(s1);
int len2 = strlen(s2); for (int i = ; i <= len1; i++)
{
for (int j = ; j <= len2; j++)
{
if (s1[i-] == s2[j-])
maxLen[i][j] = maxLen[i - ][j - ] + ;
else maxLen[i][j] = max(maxLen[i][j - ], maxLen[i-][j]);
}
} int i = len1;
int j = len2;
while(i)
{
if (maxLen[i][j] > maxLen[i - ][j])
{
if (maxLen[i][j] > maxLen[i][j - ])
ans[maxLen[i][j] - ] = s1[i - ];
else i++; //左平移
j--; //减小一个规模
}
i--; //上平移
} printf("%s\n", ans);
return ;
}
ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))的更多相关文章
- 51nod 1006 最长公共子序列Lcs 【LCS/打印path】
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 51nod 1006 最长公共子序列Lcs(经典动态规划)
传送门 Description 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是 ...
- 51Nod - 1006 最长公共子序列Lcs模板
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这 ...
- (DP)51NOD 1006 最长公共子序列&1092 回文字符串
1006 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abc ...
- 51Nod 1006 最长公共子序列Lcs问题 模板题
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这两个 ...
- 51NOD 1006 最长公共子序列 Lcs 动态规划 DP 模板题 板子
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最 ...
- 【模板】51nod 1006 最长公共子序列Lcs
[题解] dp转移的时候记录一下,然后倒着推出答案即可. #include<cstdio> #include<cstring> #include<algorithm> ...
- 【转】lonekight@xmu·ACM/ICPC 回忆录
转自:http://hi.baidu.com/ordeder/item/2a342a7fe7cb9e336dc37c89 2009年09月06日 星期日 21:55 初识ACM最早听说ACM/ICPC ...
- 用python实现最长公共子序列算法(找到所有最长公共子串)
软件安全的一个小实验,正好复习一下LCS的写法. 实现LCS的算法和算法导论上的方式基本一致,都是先建好两个表,一个存储在(i,j)处当前最长公共子序列长度,另一个存储在(i,j)处的回溯方向. 相对 ...
随机推荐
- Java中的char到底是多少个字节?
貌似一个简单的问题(也许还真是简单的)但是却把曾经自认为弄清楚的我弄得莫名其妙 char在Java中应该是16个字节byte在Java中应该是8个字节char x = '编'; //这样是合法的,输出 ...
- 自定义MapReduce的类型
package org.apache.hadoop.mapreduce.io; import java.io.DataInput; import java.io.DataOutput; import ...
- centos 安装 mysql5.7.9初始密码问题
mysql5.7.9在安装完成后会,root用户会产生一个不为空的初始密码,登陆mysql就会产生问题了,有必要修改一下登陆密码: 这是从网上找的一个方法,加以总结得出来的,亲测可以:# /etc/i ...
- Android 开发
1.Activity 子类会重载一个 protected void onCreate(Bundle savedInstanceState) { ... } 方法,其中 savedInstanceSta ...
- FireBug调试工具笔记
Firebug是网页浏览器 Mozilla Firefox下的一款开发类插件, 现属于Firefox的五星级强力推荐插件之一.它集HTML查看和编辑.Javascript控制台.网络状况监视 ...
- 使用Javascript实现返回顶部功能。
为了提高网站的浏览体验及友好度,相信大部分网站需要一个返回顶部的按钮,如果使用传统的a标记,再做一个div加上链接的话,非常麻烦,不仅每个页面都需要添加,而且不能实现非常智能的效果及简化维护时间. 下 ...
- nyoj 15 括号匹配(二)动态规划
当时看到(二)就把(一)做了, 一很容易,这道题纠结了好几天,直到今晚才看懂别人的代码谢,勉强才写出来.................... 不愧是难度6的题. #include <stdio ...
- Ubuntu使用ApkTool进行APK反编译
1.Apktool下载 http://ibotpeaches.github.io/Apktool/ 下载最新版本Apktool_2.1.1.jar 2.新建一个apktool目录,将Apktool_2 ...
- php正则
PHP代码 $str = preg_replace("/(<a.*?>)(.*?)(<\/a>)/", '\1<span class="li ...
- Extjs 学习笔记1
学习笔记 目 录 1 ExtJs 4 1.1 常见错误处理 4 1.1.1 多个js文件中有相同的控件,切换时无法正常显示 4 1.1.2 Store的使用方法 4 1.1.3 gridPanel ...