LCS的几种求法

\(LCS:\)

对于两个长度均为 \(N\) 的数列 \(A\) 和 \(B\) ,存在一个数列 \(C\) 使得 \(C\) 既是 \(A\) 的子序列有事 \(B\) 的子序列,现在需要求这个数列的最长的长度,这就是最长公共子序列。



\(solution\quad 1:\)

这道题是世界上最经典的DP题之一,我们可以知道我们做需要求的子序列中的任意一个元素在 \(A\) 和 \(B\) 中都存在,所以我们可以设出状态即 \(F[i][j]\) 表示我们已经匹配了 \(A\) 的前 \(i\) 位和 \(B\) 的前 \(j\) 位所得到的最长公共子序列。这样是很好写转移方程的:

\(F[i][j]=max\{F[i-1][j-1]+[A[i]==B[j]]\quad ,\quad F[i-1][j] \quad ,\quad F[i][j-1] \}\)

\(code\quad 1:\)

#include<iostream>
using namespace std;
int f[1001][1001],a[2001],b[2001],n,m;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            f[i][j]=max(f[i-1][j],f[i][j-1]);
            if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
        }
    cout<<f[n][m];
}


\(solution\quad2 :\)

还有一种 \(nlogn\) 的算法,它的思路很奇妙,我们用数列 \(A\) 将数列 \(B\) 里的元素离散化,就是把数列 \(B\) 里的元素在改为这个元素在数列 \(A\) 中的位置(显然我们首先还要把两个数列中不公共的元素删除)。然后我们发现只要是此时数列 \(B\) 中的上升子序列,就是两个数列的公共子序列。于是题目就变成了最长上升子序列。

然后我们回顾一下之前讲的LIS(最长上升子序列)的三种经典求法,就不难写出这道题目了!

然后有一个需要注意的地方:这两个数列的元素可能出现重复(不过这应该不影响我们的算法),只是我们离散化时还要考虑一下相同元素的顺序。这里放的代码对应洛谷P1439 【模板】最长公共子序列 (博主用的方法对应上一章最长上升子序列的第三种求法)


\(code\quad 2:\)

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int

using namespace std;

int a[100001];
int b[100001];
int n,top,l,r,mid;

inline int qr(){
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int res=ch^48;
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch^48);
    return res;
}

int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    n=qr();
    for(rg i=1;i<=n;++i) b[qr()]=i;
    for(rg i=1;i<=n;++i) a[i]=b[qr()];
    for(rg i=1;i<=n;++i){
        l=1;r=top;
        while(l<=r){
            mid=(l+r)>>1;
            if(b[mid]<=a[i]){
                l=mid+1;
                continue;
            }r=mid-1;
        } b[l]=a[i];
        if(l>top)++top;
    }printf("%d\n",top);
    return 0;
}

原文地址:https://www.cnblogs.com/812-xiao-wen/p/10996321.html

时间: 2024-10-09 20:46:53

LCS的几种求法的相关文章

逆序数的几种求法

逆序数的几种求法 白话经典算法系列之九 从归并排序到数列的逆序数对(微软笔试题)

LCS的两种解法比较

动态规划问题一般具有两个要素:最优子结构与子问题重叠. 通常在求解LCS问题时,我们都会用到两种方法: 1. momo-ization(备忘录方法) 利用了该问题的重叠子问题特性,而重叠子问题可以使用递归直接解决 0 A B C B D A B 0 0 0 0 0 0 0 0 0 B 0 0 1 1 1 1 1 1 D 0 0 1 1 1 2 2 2 C 0 0 1 2 2 2 2 2 A 0 1 1 2 2 2 3 2 B 0 1 2 2 3 3 3 4 A 0 1 2 2 3 3 4 4 所

【总结】LCA的4种求法

前言 LCA的求法有多重多样,总结下来是下面这4种.希望大家可以加油! 暴力求LCA 我们考虑dfs求出每一个点的父亲(在当前根下),然后直接先暴力跳到同一个深度,再同时跳 void dfs(int u,int f){ fa[u]=f;dep[u]=dep[f]+1; for(re int i=front[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs(v,u); } } int lca(int u,int v){ if(dep[u]

集合并卷积的三种求法

也许更好的阅读体验 本文主要内容是对武汉市第二中学吕凯风同学的论文<集合幂级数的性质与应用及其快速算法>的理解 定义 集合幂级数 为了更方便的研究集合的卷积,引入集合幂级数的概念 集合幂级数也是形式幂级数的一种,只是集合的一种表现形式,无需考虑收敛或发散的含义 定义一个集合 \(S\) 的集合幂级数为 \(f\) ,那么我们就可以把集合 \(S\) 表示为如下形式 \(\begin{aligned}f=\sum _{T\subseteq S}f_{T}\cdot x^{T}\end{align

LCA的两种求法

HDU 2586 题意:一棵树,多次询问任意两点的路径长度. LCA:最近公共祖先Least Common Ancestors.两个节点向根爬,第一个碰在一起的结点. 求出x, y的最近公共祖先lca后,假设dist[x]为x到根的距离,那么x->y的距离为dist[x]+dist[y]-2*dist[lca] 求最近公共祖先解法常见的有两种 1, tarjan+并查集 2,树上倍增 首先是树上倍增. 1 #include <cstdio> 2 #include <cstring&

逆序对的两种求法(复习)

逆序对 对于一个数列\(a_1...a_n\),定义一有序对\((i,j)\)当且仅当\(i<j\)且\(a_i>a_j\)为逆序对.接着我们来考虑怎么求 *1. 归并排序 回顾归并排序的过程,将当且的数列\([l,r]\)分成两个长度相等的部分\([l,mid]\)和\([mid+1,r]\),分治下去排序,每次合并的代价是区间的长度,所以得到时间复杂度为: \[ T(n)=2T(\frac{n}{2})+O(n) \] 根据\(master\)定理可知时间复杂度为\(\Theta(nlog

[BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)

题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+1..|s|], |s| 为 s 的长度. 题解 这题的描述很短,给人一种很可做的假象. 暴力1:每次对区间lr做一次KMP,求出border数组,复杂度nq. 暴力2:构建后缀自动机,用线段树合并维护出right集合考虑到两个串的最长后缀为他们在parent树上的LCA的len,所以我们可以在pa

[BJWC2018]Border 的四种求法

description luogu 给一个小写字母字符串\(S\),\(q\)次询问每次给出\(l,r\),求\(s[l..r]\)的\(Border\). solution 我们考虑转化题面:给定\(l,r\),求满足\(lcs(i,r)\ge i-l+1\)的最大的\(i\). 对于\(lcs(i,r)\),考虑对\(S\)构建\(SAM\),那么我们知道\(lcs\)的可能取值就是从后缀\(r\)所代表的节点沿着\(fail\)树到根节点的路径上节点的\(len\)的取值. 那么我们得到了

斐波那契的四种求法

首先看一下斐波那契的矩阵表示: 数列的递推公式为:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2)(n>=3)  用矩阵表示为: 进一步,可以得出直接推导公式: #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #define N 1000 using