luogu2516題解

LiJoQiao發表於2024-09-10

隨機說話

第一次交的時候寫的版本是這個
下面在 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;
}