首页 > 其他分享 >[题解]P2516 [HAOI2010] 最长公共子序列——求LCS个数

[题解]P2516 [HAOI2010] 最长公共子序列——求LCS个数

时间:2024-03-31 21:15:13浏览次数:15  
标签:5010 LCS int 题解 P2516 序列 cases 最长

P2516 [HAOI2010] 最长公共子序列

总的来说,这道题确实很精妙,难度也不小,耗费了不少时间去调。本来想过用容斥的思想,却因为当时理解不深没有继续思考就放弃了。学会了思路后对\(LCS\)有了更深层次的理解。

题意简述

给定\(A,B\)两个字符串(以.结尾),求它们的最长公共子序列的长度个数(取模\(10^8\))。

注意:两个字符串可能有多个最长公共子序列,同一个最长公共子序列,哪怕放的位置不同,也算作两个。

样例

输入:

ABCBDAB.
BACBBD.

输出:

4
7

样例解释

长度为\(4\):ABBD\(*1\),ACBB\(*1\),ACBD\(*2\),BCBB\(*1\),BCBD\(*2\)。

思路简述

根据\(LCS\)算法的思想,我们画出以下表格(注意路径不唯一,所以每个格子不止一个箭头)。右下角的暗红色数字是\(f\)数组,记录长度。

第一问就是\(f[n][m]\)的值(\(n,m\)表示\(A,B\)的长度)。

第二问怎么解决呢?结果显然就是从\((n,m)\)开始,根据格子上的指示回溯的路径条数。当时以为这里用记忆化搜索直接计数就能得到正确答案,但其实这样做可能会有重复。如下图:

从右下开始走,左-上-左上上-左-左上的效果是一样的,都是选择了C加入子序列。而左上-左上的效果不同,选择了DC加入子序列。由此可以看出并不是路径不同最终子序列就不同。应该是路径中经过的位置不同,最终子序列就不同(注意不同的子序列可能字符串表示相同,前面题意简述说明了)。

所以搜索的方法会有重复计算的错误,考虑和\(f\)一样,定义一个\(g\)数组,\(g[i][j]\)表示\(A,B\)长度分别为\(i,j\)的前缀\(LCS\)的个数。

  • 如果\(a[i]=b[j]\),说明此处有一个,那么应该有这样子\(4\)种情况:

    也就是说,如果\(a[i]=b[j]\),首先\(g[i][j]+=g[i-1][j-1]\),表示从这个格子往走的个数(长度为\(3+1=4\)),如果\(f[i-1][j]=f[i][j]\)或者\(f[i][j-1]=f[i][j]\),说明我们也可以不选,而是选择,仍然能选\(4\)个。

  • 如果\(a[i]\neq b[j]\),那么此处没有。判断方法相同:

    • 如果\(f[i-1][j]=f[i][j]\)说明有,\(g[i][j]+=g[i-1][j]\);
    • 如果\(f[i][j-1]=f[i][j]\)说明有,\(g[i][j]+=g[i][j-1]\)。

    但是如果\(f[i-1][j-1]=f[i][j]\)(\(a[i]\neq b[j]\)不代表\(f[i-1][j-1]\neq f[i][j]\)),说明左-上-的操作可能有重复计算(就是第二张图那种,左-上上-左都能到达\((i-1,j-1)\),又因为\(f[i-1][j-1]=f[i][j]\),所以都是最长。这样就重复计算了),需要\(g[i][j]-=g[i-1][j-1]\)。


综上,我们得到状态转移方程:

\(f[i][j]= \begin{cases} f[i-1][j-1]+1 & if\ a[i]=b[j]\\ max(f[i-1][j],f[i][j-1]) & otherwise \end{cases}\)

\(g[i][j] \begin{cases} =1 & if\ i=0\ or\ j=0\\ otherwise:\\ +=g[i-1][j-1] & if\ a[i]=b[j]\\ +=g[i-1][j] & if\ f[i-1][j]=f[i][j]\\ +=g[i][j-1] & if\ f[i][j-1]=f[i][j]\\ -=g[i-1][j-1] & if\ f[i-1][j-1]=f[i][j]\\ \end{cases}\)

注意事项

  • 别忘取模\(10^8\)。
  • 开\(5000*5000\)会炸空间,所以需要滚动数组优化空间。
  • 虽然递推有减法,但我们不需要担心出现负数影响取模。因为我们一定会作加法,而且加的数所在位置一定不在减的数所在位置左或上(还是比较容易理解的)。

具体见代码~

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define mod 100000000ll
using namespace std;
string a,b;
int n,m,f[5010][5010];
int g[5010][5010];
signed main(){
	cin>>a>>b;
	n=a.size()-1,m=b.size()-1;
	a=' '+a,b=' '+b;
	bool cur=1,pre=0;
	for(int i=0;i<=m;i++) g[0][i]=1;
	g[1][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			g[cur][j]=0;
			if(a[i]==b[j]) f[cur][j]=f[pre][j-1]+1;
			else f[cur][j]=max(f[pre][j],f[cur][j-1]);
			if(a[i]==b[j]) g[cur][j]+=g[pre][j-1];
			if(f[cur][j]==f[cur][j-1]) g[cur][j]+=g[cur][j-1];
			if(f[cur][j]==f[pre][j]) g[cur][j]+=g[pre][j];
			if(f[cur][j]==f[pre][j-1]) g[cur][j]-=g[pre][j-1];
			g[cur][j]%=mod;
		}
		cur=!cur,pre=!pre;
	}
	cout<<f[pre][m]<<endl<<g[pre][m];
	return 0;
}

\[[Fin.] \]

欢迎在评论区留下你的问题或建议。

标签:5010,LCS,int,题解,P2516,序列,cases,最长
From: https://www.cnblogs.com/Sinktank/p/18105335

相关文章

  • 0324-0331题解反思
    最近我突然发现,写题解是常常会遗忘的,然而题目中的一些技巧才是永恒的,那么接下来的题解,我应该对以前的题目含有的这些技巧进行一些深刻的复盘。知识点模块1.当我们要实现一个图形的字符串的倒置,比如把福倒过来,我们可以进行以下的操作:先进行行的交换,在进行列的倒置,我们需要用到s......
  • 【C++实验1】学生成绩信息管理系统题解
    【问题描述】编写一个基于结构体得学生成绩信息管理系统。主要功能如下:1.用结构体存放所有数据。2.每个功能都用函数实现。3.输入10个学生的学号和三门课程的成绩。4.计算每个学生的总分。5.按总分从高到低排序。6.加上名次一列。7.输出最后的二维表格样式的成......
  • 题解 P9981 [USACO23DEC] Minimum Longest Trip G
    【洛谷专栏】题意\(N\)个点\(M\)条边的有向无环图,对于每一个点\(i\)你需要求出一条从\(i\)出发的最长路径且路径上边权组成的序列字典序最小。求每一条路径的长度和边权和。分析最长的路径很好求,在DAG上拓扑排序后动态规划即可。(具体的实现可以参考OI-Wiki)现在的......
  • Minlexes题解
    \(\texttt{ProblemLink}\)简要题意在一个字符串\(s\)中,对于每个后缀,任意删掉一些相邻的相同的字符,使得字符串字典序最小。注意:删掉之后拼起来再出现的相邻相同字符不能够删除。思路倍增好题。发现存在局部最优解(最优子结构),并且可以转移到其它结点,可以考虑使用dp。那就......
  • Tomcat启动失败,窗口一闪而过问题解决
    在启动Tomcat时窗口一闪而过,解决方法:在Tomcat安装目录\bin下启动cmd,或在C盘启动后跳转到Tomcat安装目录\bin,输入startup.bat(一定要先做这步,确保具体问题,再根据具体问题百度,不然又是配置JRE,又是配置Tomcat环境变量,最后做了无用功),如果显示如下:先确保java环境变量没问题,我的java......
  • 题解:AT_arc175_b [ARC175B] Parenthesis Arrangemen
    前言警示后人:字符串最大长度为\(65535\),会\(RE\)!!!\(10^7\)会爆栈!!!题意给出一个括号序列\(s\),有两种操作方式,交换两个字符需要花费\(A\),直接修改一个字符需要花费\(B\),求使这个序列合法需要的最小花费。分析我们可以先将\(s\)中能匹配的括号序列消除掉(即括号匹配......
  • [蓝桥杯] 管道 java题解
    importjava.util.*;/***管道*其实这道题核心根本不用管管道左边的如何,我们可以把左边当成注水口*/publicclassMain{staticintn;staticint[][]pipes;//阀门安排的地方staticintlen;//管道长度publicstaticvoidmain(String[]a......
  • AtCoder Beginner Contest 347 A-F 题解
    A-DivisibleQuesiton给你正整数\(N\)和\(K\),以及长度为\(N\)的序列\(A\)。提取\(A\)中所有是\(K\)倍数的元素,除以\(K\),并打印商。Solution判断\(A_i\%K\)的值是否为\(0\),如果非\(0\)则表示可以整除Code#include<bits/stdc++.h>usingnamespacest......
  • CCF-CSP真题《202309-3 梯度求解》题解
    题目string转longlong忘记处理负数卡了半天,服了#include<iostream>#include<cstdio>#include<cstring>#include<sstream>typedeflonglongll;usingnamespacestd;intn,m,temp;lla[302];stringf,x,b;llmod=1e9+7;structnode{ stringcon; n......
  • 矩阵匹配【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-矩阵匹配从一个NM(N<=M)的矩阵中选出N个数,任意两个数字不能在同一行或同一列,求选出来的N个数中第K大的数字的最小值是多少。输入描述:输入矩阵要求:1<=K<=N<=M<=150输入格式:NMKNM矩阵输出描述:N*M的矩阵中可以选出M!/N!种组合数组,每个组合数组中第K大的数中的......