luogu2516题解

随机说话

第一次交的时候写的版本是这个
下面在 sum 的计算上少了个 else,遂出错。
以后写的时候还是尽量简单点,主要是调试的时候好调。

题目分析

题目里面的公共子序列就是可以不连续可以为空的在字符串里出现顺序相同的子串。

对于一个公共子序列,在找到最后一个公共的字符的时候,是由前面的公共子序列转移过来的。

对于两个字符串前面的子串求出的所有公共子序列,我们发现长度不是最大的公共子序列对答案没有贡献,所以没有必要转移其他长度的公共子序列的个数。

循环枚举的过程中我们的求解过程是有从字符串前到字符串后的顺序的,在状态转移的过程中不能忽略这个字符串位置对状态转移的影响。

在循环枚举两个字符串 \(s1,s2\) 位置 \(s1_i,s2_j\) 的时候,我们发现公共子序列所取字符的位置对答案没有贡献,故也可从状态中省去。

我们令 \(dp_j\) 为以 \(s1_i,s2_j\) 结尾的最长公共子序列的状态(包括最长长度和个数),在枚举到 \(s1_i,s2_j\) 的时候 \(dp_{i,j}=\{\max(dp_{i-1,l}.len(l\in\left[1,j\right)))+1,\sum\limits_{dp_{i-1,k}=\max(dp_{i-1,l}(l\in\left[1,j\right)))}dp_{i-1,k}.cnt\}\),这样就可以求出此题的答案。

由于一维可以省去,故用了类似完全背包的方法倒着跑来省下内存。

在求解中间和的过程可以像前缀和一样预处理一下,使时间复杂度由 \(O(n^3)\) 变为 \(O(n^2)\)

代码如下。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
constexpr int MOD=1e8,MAXN=5000+10;
char s1[MAXN],s2[MAXN];
int n1,n2;
pii sum[MAXN],dp[MAXN];//fi=len,se=cnt
#define fi first
#define se second
int main(){
	scanf("%s%s",s1+1,s2+1);
	n1=strlen(s1+1);
	n2=strlen(s2+1);
	sum[0]={0,1};
	for(int i=1;i<=n1;++i){
		for(int j=1;j<=n2;++j){
			if(dp[j].fi>sum[j-1].fi){
				sum[j]=dp[j];
			}else if(dp[j].fi==sum[j-1].fi){
				sum[j].fi=sum[j-1].fi;
				sum[j].se=(sum[j-1].se+dp[j].se)%MOD;
			}else{
				sum[j]=sum[j-1];
			}
		}
		for(int j=n2;j;--j){
			if(s1[i]==s2[j]){
				if(sum[j-1].fi+1>dp[j].fi){
					dp[j].fi=sum[j-1].fi+1;
					dp[j].se=sum[j-1].se;
				}else if(dp[j].fi==sum[j-1].fi+1){
					dp[j].se=(dp[j].se+sum[j-1].se)%MOD;
				}
			}
		}
	}
	printf("%d\n%d\n",dp[n2].fi-1,dp[n2].se);
	return 0;
} 
posted @ 2024-09-10 18:09  LiJoQiao  阅读(4)  评论(0编辑  收藏  举报