对最长公共子序列(LCS)等一系列DP问题的研究
LIS问题:
设\(f[i]\)为以\(a[i]\)结尾的最长上升子序列长度,有:
\]
可以用树状数组优化至\(O(nlogn)\)
基于排列的LCS问题(\(a,b\)均为排列,即一个元素不会出现多次):
设\(pos_i\)为\(a_i\)在\(b\)中出现的位置,即\(a_i=b_pos_i\)。
\(a\)的一个子序列\(a_p_1,a_p_2,...,a_p_m\)是\(a,b\)的公共子序列等价于\(pos_p_1<pos_p_2<...<pos_p_m\)
求一个LIS即可。
一般LCS问题:
- 经典解法:
设\(f[i][j]\)表示只考虑\(a\)中前\(i\)个,\(b\)中前\(j\)个的最长公共子序列长度,有:
\begin{aligned}
& f[i-1][j-1] & a[i]=b[j]\\
& max(f[i-1][j],f[i][j-1]) & a[i]!=b[j]\\
\end{aligned}
\right.\]
十分简单,但是还有一种稍微复杂但是拓展性更高的做法:
设$f[i][j]$表示只考虑$a$中前$i$个,$b$中前$j$个并且$b_j$已经和$a_1,...,a_i$中的某一个匹配的最长公共子序列长度,有:
\begin{aligned}
& f[i-1][j] & a[i]!=b[j]\\
& max(f[i-1][k]+1) & a[i]==b[j],k<j\\
\end{aligned}
\right.\]
为什么说这样拓展性更好?来看这样一道题
题目要求最长上升公共子序列,不能直接用LCS的经典解法了,但是我们仔细思考一下,发现如果我们用上面的转移方程,我们只需要在从\(f[i-1][k]\)转移到\(f[i][j]\)时,只需要保证\(b[k]<b[j]\)即可,所以我们得到新的转移方程:
\begin{aligned}
& f[i-1][j] & a[i]!=b[j]\\
& max(f[i-1][k]+1) & a[i]==b[j],k<j&&b[k]<b[j]\\
\end{aligned}
\right.\]
又因为当\(a[i]==b[j]\)时,\(b[k]<b[j]\)等价于\(b[k]<a[i]\),在转移枚举\(j\)时对所有\(b[k]<a[i]\)的\(f[i-1][k]\)记录一个前缀\(max\)即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 5007
int f[N],a[N],b[N];
int main()
{
int i,j,n;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
scanf("%d",&b[i]);
int maxx=0,ans=0;
for(i=1;i<=n;i++)
{
maxx=0;
for(j=1;j<=n;j++)
{
if(b[j]==a[i])f[j]=max(f[j],maxx+1);
else if(b[j]<a[i])maxx=max(maxx,f[j]);
ans=max(ans,f[j]);
}
}
printf("%d\n",ans);
return 0;
}
当然还有对于一般LCS问题的\(O(nlogn)\)解法(不严格),同样可以拓展至此题。
在排列中,\(a\)中的每一个元素唯一对应\(b\)中的一个元素,但在一般的LCS问题中不是这样,一个元素可以对应多个元素。
怎么办呢?我们把\(a\)中每个元素在\(b\)中对应位置的集合拿出来,比如\(a={3,3,2,4},b={2,3,3,5}\),那么\(a\)中元素\(3\)对应的位置集合就是{2,3},将每个元素对应位置降序排列,再放回原序列中,对得到的新序列做一个LIS,就是最长公共子序列的长度,比如上面那个例子,a中每个元素对应集合为\(\{2,3\},\{2,3\},\{1\},\{\}\),得到的新序列就是\(3,2,3,2,1\),它的最长上升子序列是\(2,3\),即\(a,b\)的最长公共子序列是由\(b_2,b_3\)组成的,这时再套用LIS的\(O(nlogn)\)做法即可,但是这样复杂度不是严格\(O(nlogn)\)的,因为每个\(a\)中的元素最多对应\(n\)个b中的元素,最坏复杂度达到\(O(n^2 log n)\)。
至于这种做法到上面这题的拓展,因为你要保证你在\(a\)中选择的元素在\(b\)中的位置是递增的,同时要保证这些元素的值本身也是递增的,也就是这样:
\]
发现是转移一个二维偏序关系,二维数点即可,复杂度是不严格的\(O(nlog^2 n)\)。
对最长公共子序列(LCS)等一系列DP问题的研究的更多相关文章
- 1006 最长公共子序列Lcs
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdks ...
- 动态规划之最长公共子序列LCS(Longest Common Subsequence)
一.问题描述 由于最长公共子序列LCS是一个比较经典的问题,主要是采用动态规划(DP)算法去实现,理论方面的讲述也非常详尽,本文重点是程序的实现部分,所以理论方面的解释主要看这篇博客:http://b ...
- 编程算法 - 最长公共子序列(LCS) 代码(C)
最长公共子序列(LCS) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符 ...
- C++版 - Lintcode 77-Longest Common Subsequence最长公共子序列(LCS) - 题解
版权声明:本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C++版 - L ...
- POJ 1458 Common Subsequence(最长公共子序列LCS)
POJ1458 Common Subsequence(最长公共子序列LCS) http://poj.org/problem?id=1458 题意: 给你两个字符串, 要你求出两个字符串的最长公共子序列 ...
- 51Nod 1006:最长公共子序列Lcs(打印LCS)
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 51nod 1006 最长公共子序列Lcs 【LCS/打印path】
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 题解报告:hdu 1159 Common Subsequence(最长公共子序列LCS)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1159 Problem Description 给定序列的子序列是给定的序列,其中有一些元素(可能没有) ...
- 每日一题-——最长公共子序列(LCS)与最长公共子串
最长公共子序列(LCS) 思路: 代码: def LCS(string1,string2): len1 = len(string1) len2 = len(string2) res = [[0 for ...
- 51nod 1006:最长公共子序列Lcs
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
随机推荐
- Vue.js 源码分析(十四) 基础篇 组件 自定义事件详解
我们在开发组件时有时需要和父组件沟通,此时可以用自定义事件来实现 组件的事件分为自定义事件和原生事件,前者用于子组件给父组件发送消息的,后者用于在组件的根元素上直接监听一个原生事件,区别就是绑定原生事 ...
- 使用eclipse git插件合并merge部分代码方法
当有一个父项目,它的下面有多个子项目:或者一个项目下边,只想合并部分路径,甚至部分文件的内容,使用下边的方法可以达到目的,特此记录: 1.主项目右键 -> team -> remove f ...
- kubernetes之coredns玩法
一.概述 新版本的kubernetes默认使用了coredns,这里就不赘述了.直达车:https://coredns.io/.https://kubernetes.io/docs/tasks/adm ...
- 【Python3爬虫】最新的12306爬虫
一.写在前面 我在以前写过一次12306网站的爬虫,当时实现了模拟登录和查询车票,但是感觉还不太够,所以对之前的代码加以修改,还实现了一个订购车票的功能. 二.主要思路 在使用Selenium做模拟登 ...
- (一)RFB协议概述
(文章是基于刚刚做过的一个项目,对相关知识点进行总结回顾.主要内容均是对之前收集资料的总结概括,很多内容转载自其它地方.因为时间比较长,没有一一记录转载地址,特此感谢!) 一.简介 RFB(远程帧缓冲 ...
- 解决Hangfire 导致服务器内存飙涨
最近因为项目需要调度作业服务,之前看张队推荐过一篇https://www.cnblogs.com/yudongdong/p/10942028.html 故直接拿过来实操,发现很好用,简单.方便 执行 ...
- 《Netty 权威指南(第2 版)》目录
图书简介:<Netty 权威指南(第2 版)>是异步非阻塞通信领域的经典之作,基于最新版本的Netty 5.0 编写,是国内很难得一见的深入介绍Netty 原理和架构的书籍,也是作者多年实 ...
- 在ASP.NET MVC中创建自定义模块
创建模块 module是实现了System.Web.IHttpModule接口的类.该接口定义了两个方法: Init:当模块初始化时被调用,传入的参数为HttpApplication对象,用于注册请求 ...
- Python中的@函数装饰器到底是什么?
在解释@函数装饰器之前,先说一下,类中的类方法和静态方法. 在Python中完全支持定义类方法.静态方法.这两种方法很相似,Python它们都使用类来调用(ps:用对象调用也可以). 区别在于:Pyt ...
- js 数组去重总结
es6 set ES6 提供了新的数据结构 Set.它类似于数组,但是成员的值都是唯一的,没有重复的值. let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique = ...