2021.12.10 P2516 [HAOI2010]最长公共子序列(动态规划+滚动数组)

https://www.luogu.com.cn/problem/P2516

题意:

给定字符串 \(S\) 、 \(T\) ,都以 \(.\) 结尾,求 \(S\) 、 \(T\) 最长公共子序列的长度及个数。

分析:

一顿操作猛如虎,一看分数250……爆零了。原本就没准备拿几分,结果令人心塞。

第一问就是求最长公共子序列长度,数据范围比较小, \(O(n^2)\) 就行,上来就是一顿树状数组+LIS,忽视了里面有重复出现的字符。正解就是普普通通的DP,啥也没加,因为也加不了啥。

第二问实际上是第一问的深入思考(大雾)。

先来看求LCS的状态转移方程:

\[f[i][j]=\begin{cases}
f[i-1][j-1]+1,&S_i==T_j\\
\max(f[i][j-1],f[i-1][j]),&S_i!=T_j
\end{cases}
\]

对于第一个转移方程,相当于是 \(S\) 的前 \(i\) 个字符全部出现在LCS中, \(T\) 的前 \(j\) 个字符同样全部出现在LCS中(情况一);

对于第二个转移方程,分两种情况:

情况a: \(S\) 的前 \(i\) 个字符全部出现在LCS中, \(T\) 的前 \(j-1\) 个字符全部出现在LCS中(情况二);

情况b: \(S\) 的前 \(i-1\) 个字符全部出现在LCS中, \(T\) 的前 \(j\) 个字符全部出现在LCS中(情况三);

但是还有特殊情况,即 \(S\) 的前 \(i-1\) 个字符全部出现在LCS中, \(T\) 的前 \(j-1\) 个字符同样全部出现在LCS中(情况四)。

\(g[i][j]\) 为求方案的数组。

\[g[i][j]+=\begin{cases}
g[i-1][j-1],&情况一\\
g[i][j-1],&情况二\\
g[i-1][j],&情况三\\
-g[i-1][j-1],&情况四
\end{cases}
\]

情况四是因为当在计算 \(f[i][j]\) 时,已经被 \(f[i][j-1]\) (按照情况二)和 \(f[i-1][j]\) (按照情况三)计算两遍了,所以减去。

至于其他情况为什么是加号而不是等号(我脑子抽抽,第一次写成等号,于是我挂了,shift!),那是因为这个位置会经过四重情况层层检验计算(大雾)。

代码如下:

100pts:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std; const int N=5e3+10;
const int mod=1e8;
int n,m,a[N],b[N],f[3][N],g[3][N]; int main(){
string s;cin>>s;
n=s.length()-1;
for(int i=1;i<=n;i++)a[i]=s[i-1]-'A'+1,g[0][i]=1;
cin>>s;
m=s.length()-1;
for(int i=1;i<=m;i++)b[i]=s[i-1]-'A'+1,g[0][i]=1;
g[1][0]=g[0][0]=1;
int k=1;
for(int i=1;i<=n;i++,k^=1){
for(int j=1;j<=m;j++){
g[k][j]=0;
if(a[i]==b[j])f[k][j]=f[k^1][j-1]+1;
else f[k][j]=max(f[k^1][j],f[k][j-1]);
if(a[i]==b[j])g[k][j]+=g[k^1][j-1];
if(f[k][j]==f[k][j-1])g[k][j]+=g[k][j-1];
if(f[k][j]==f[k^1][j])g[k][j]+=g[k^1][j];
if(f[k][j]==f[k^1][j-1])g[k][j]-=g[k^1][j-1];
g[k][j]=(g[k][j]%mod+mod)%mod;
}
}
cout<<f[k^1][m]<<endl<<g[k^1][m];
/*cout<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)cout<<g[i][j]<<" ";
cout<<endl;
}*/
return 0;
}

0pts:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std; const int N=5e3+10;
int n,top,a[N],t[N],cnt[N];
int cap[30][N]; inline int lowbit(int x){
return x&-x;
}
inline void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i))t[i]=max(t[i],k);
}
inline int query(int x){
int fin=0;
for(int i=x;i>0;i-=lowbit(i))fin=max(fin,t[i]);
return fin;
}
inline void find(){
for(int i=1;i<=10;i++)cout<<query(i)<<" ";cout<<endl;
} int main(){
string s;
cin>>s;
for(int i=1;i<s.length();i++){
int x=s[i-1]-'A'+1;
++cap[x][0];
cap[x][cap[x][0]]=++top;
}
for(int i=1;i<=26;i++)cap[i][0]=0;
cin>>s;
n=s.length()-1;
int maxn=0,num=0;
for(int i=1;i<s.length();i++){
int x=s[i-1]-'A'+1;
++cap[x][0];
if(!cap[x][cap[x][0]])cap[x][cap[x][0]]=++top;
a[i]=cap[x][cap[x][0]];
int len=query(a[i]-1)+1;
if(len==maxn)++num;
else if(len>maxn)maxn=len,num=1;
add(a[i],len);
//cout<<i<<" "<<a[i]<<" "<<len<<" "<<query(a[i]-1)<<endl;
//find();//
}
//for(int i=1;i<s.length();i++)cout<<a[i]<<" ";cout<<endl;
cout<<maxn<<endl<<num;
return 0;
}

2021.12.10 P2516 [HAOI2010]最长公共子序列(动态规划+滚动数组)的更多相关文章

  1. 洛谷P2516 [HAOI2010]最长公共子序列(LCS,最短路)

    洛谷题目传送门 一进来就看到一个多月前秒了此题的ysn和YCB%%% 最长公共子序列的\(O(n^2)\)的求解,Dalao们想必都很熟悉了吧!不过蒟蒻突然发现,用网格图貌似可以很轻松地理解这个东东? ...

  2. 洛谷 P2516 [HAOI2010]最长公共子序列

    题目传送门 解题思路: 第一问要求最长公共子序列,直接套模板就好了. 第二问要求数量,ans[i][j]表示第一个字符串前i个字符,第二个字符串前j个字符的最长公共子序列的数量 如果f[i][j]是由 ...

  3. P2516 [HAOI2010]最长公共子序列 题解(LCS)

    题目链接 最长公共子序列 解题思路 第一思路: 1.用\(length[i][j]\)表示\(a\)串的前\(i\)个字符与\(b\)串的前\(j\)个字符重叠的最长子串长度 2.用\(num[i][ ...

  4. POJ 1159 Palindrome-最长公共子序列问题+滚动数组(dp数组的重复利用)(结合奇偶性)

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

  5. poj1159--Palindrome(dp:最长公共子序列变形 + 滚动数组)

    Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 53414   Accepted: 18449 Desc ...

  6. luogu P2516 [HAOI2010]最长公共子序列

    传送门 首先那个\(O(n^2)\)的dp都会吧,不会自己找博客或者问别人,或是去做模板题(误) 对以下内容不理解的,强势推荐flash的博客 我们除了原来记录最长上升子序列的\(f_{i,j}\), ...

  7. 洛谷P2516 [HAOI2010]最长公共子序列

    题目描述 字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X="x0,x1,-,xm-1",序列Y=& ...

  8. Luogu P2516 [HAOI2010]最长公共子序列 DP

    首先$LIS$显然:$f[i][j]=max(f[i][j-1],f[i-1][j],(a[i]==b[j])*f[i-1][j-1])$ 考虑如何转移数量: 首先,不管$a[i]$是否等于$b[j] ...

  9. P2516 [HAOI2010]最长公共子序列

    传送门 看到数据范围,显然 $n^2$ 的 $dp$... 设 $f[i][j]$ 表示 $A$ 串考虑了前 $i$ 位,$B$ 串考虑了前 $j$ 位,最优情况下的方案数 但是好像没法判断转移来的是 ...

随机推荐

  1. 初学者都能学会的ElasticSearch入门实战

    大家好,我是咔咔 不期速成,日拱一卒 项目中准备使用ElasticSearch,之前只是对ElasticSearch有过简单的了解没有系统的学习,本系列文章将从基础的学习再到深入的使用. 咔咔之前写了 ...

  2. 6月21日 Django ORM那些相关操作(表关联、聚合查询和分组查询)

    一.ForeignKey操作 正向查找 对象查找(跨表) 语法: 对象.关联字段.字段   示例: book_obj = models.Book.objects.first() # 第一本书对象 pr ...

  3. JAVA视频笔记(一)

    搭建pho开发环境与框架图 韩顺平 第一章: No1  关于文件以及文件夹的管理 将生成的文本文档做成详细信息的形式,显示文件修改时间以及文件大小,便于文件查看和管理,也是对于一名IT人士高效能工作的 ...

  4. MindSpore尝鲜之Vmap功能

    技术背景 Vmap是一种在python里面经常提到的向量化运算的功能,比如之前大家常用的就是numba和jax中的向量化运算的接口.虽然numpy中也使用到了向量化的运算,比如计算两个numpy数组的 ...

  5. B+树叶子节点数据如何存储,以及如何查找某一条数据

    MySQL索引背后的数据结构及算法原理 https://www.kancloud.cn/kancloud/theory-of-mysql-index  非常好 根据一条sql  如何查看索引结构等信息 ...

  6. 构造器注入和 setter 依赖注入,那种方式更好?

    每种方式都有它的缺点和优点.构造器注入保证所有的注入都被初始化,但是 setter 注入提供更好的灵活性来设置可选依赖.如果使用 XML 来描述依赖, Setter 注入的可读写会更强.经验法则是强制 ...

  7. 32 位和 64 位的 JVM,int 类型变量的长度是多数?

    32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4个字节.

  8. Iterator 和 ListIterator 有什么区别?

    1.ListIterator 可以在遍历的时候,调用add()添加元素 2.ListIterator提供了更多的一些方法,如previous().hasPrevious() 等

  9. 『现学现忘』Docker基础 — 36、CMD指令和ENTRYPOINT指令的区别

    目录 1.CMD指令和ENTRYPOINT指令说明 2.CMD指令只有最后一条生效的原因 3.CMD指令演示 4.ENTRYPOINT指令演示 5.总结 CMD指令和ENTRYPOINT指令作用都是指 ...

  10. C语言之API

    C语言之API 1.输入(控制台输入) scanf("%d,%d",&a,&b); 2.输出(打印数值) printf("max=%d\n",c ...