当前位置: 首页 > news >正文

LCS 问题解释

最长公共子序列问题(Longest Common Subsequence),该问题可以表述为,在 A , B A,B A,B 中找出一段子序列 x x x,使得 x x x 既是 A A A 的子序列,又是 B B B 的子序列。

你可以理解为,在两个序列中分别删除一些元素(剩下的不一定连续),使得两个序列的剩余部分相同且长度最长。

暴力解法可以用 DFS,但是时间复杂度为 O ( ∣ A ∣ × 2 ∣ B ∣ ) O(|A|\times 2^{|B|}) O(A×2B),不能够满足我们的需求。

考虑动态规划。

定义:设 d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共子序列。

答案为 d p n , m dp_{n,m} dpn,m

状态转移:

d p i , j = { d p i − 1 , j − 1 A i = B j max ⁡ ( d p i − 1 , j , d p i , j − 1 ) otherwise dp_{i,j}=\begin{cases} dp_{i-1,j-1}& A_i=B_j\\ \max(dp_{i-1,j},dp_{i,j-1})&\text{otherwise} \end{cases} dpi,j={dpi1,j1max(dpi1,j,dpi,j1)Ai=Bjotherwise

但如果需要输出任意合法路径(题目:51Nod - 1006),怎么办?

递归倒推回去。

f ( x , y ) f(x,y) f(x,y) 表示 A A A 序列前 x x x 个和 B B B 序列前 y y y 个的匹配情况。

如果 A x = B y A_x=B_y Ax=By,那么说明选择了这个点,输出 A x A_x Ax(或 B y B_y By),同时调用 f ( x − 1 , y − 1 ) f(x-1,y-1) f(x1,y1)

否则,如果 d p x − 1 , y > d p x , y − 1 dp_{x-1,y}>dp_{x,y-1} dpx1,y>dpx,y1,调用 f ( x − 1 , y ) f(x-1,y) f(x1,y)

否则调用 f ( x , y − 1 ) f(x,y-1) f(x,y1)

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[1005][1005];
string s,t;
void print(int x,int y){if(!x||!y){return;}if(s[x]==t[y]){print(x-1,y-1);cout<<s[x];}else{if(dp[x-1][y]>dp[x][y-1]){print(x-1,y);}else{print(x,y-1);}}
}
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>s>>t;s=' '+s,t=' '+t;for(int i=1;i<s.size();i++){for(int j=1;j<t.size();j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}print(s.size()-1,t.size()-1);return 0;
}

HDU-1159 Common Subsequence

最基础的那道题,本来应该放前面的。

d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共子序列。

答案为 d p n , m dp_{n,m} dpn,m

状态转移:

d p i , j = { d p i − 1 , j − 1 A i = B j max ⁡ ( d p i − 1 , j , d p i , j − 1 ) otherwise dp_{i,j}=\begin{cases} dp_{i-1,j-1}& A_i=B_j\\ \max(dp_{i-1,j},dp_{i,j-1})&\text{otherwise} \end{cases} dpi,j={dpi1,j1max(dpi1,j,dpi,j1)Ai=Bjotherwise

注意:*.size() 类型为无符号整型,不管在哪个容器都一样。

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[1005][1005]; // 设 dp[i][j] 表示 A 串前 i 个字符和 B 串前 j 个字符的最长公共子序列
string s,t;
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);while(cin>>s>>t){s=' '+s,t=' '+t;for(int i=1;i<s.size();i++){for(int j=1;j<t.size();j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}cout<<dp[s.size()-1][t.size()-1]<<'\n';}return 0;
}

U118717 最长公共上升子序列

注意:该做法不太严谨,数据太水,本来应该会 TLE 的。

d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共上升子序列。

答案: max ⁡ { d p i , j } \max\{dp_{i,j}\} max{dpi,j}

计算前先进行 d p i , j = d p i − 1 , j dp_{i,j}=dp_{i-1,j} dpi,j=dpi1,j

A i = B j A_i=B_j Ai=Bj,那么 d p i , j = max ⁡ ( d p [ i ] [ j ] , 1 ) dp_{i,j}=\max(dp[i][j],1) dpi,j=max(dp[i][j],1)

枚举 k ( k ∈ [ 1 , j − 1 ] ) k(k\in[1,j-1]) k(k[1,j1]),若 b k < b j b_k<b_j bk<bj,则:

d p i , j = max ⁡ ( d p i , j , d p i , k + 1 ) dp_{i,j}=\max(dp_{i,j},dp_{i,k}+1) dpi,j=max(dpi,j,dpi,k+1)

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,dp[3005][3005],a[3005],b[3005],ans;
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){cin>>b[i];}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){dp[i][j]=dp[i-1][j];if(a[i]==b[j]){dp[i][j]=max(dp[i-1][j],1ll);for(int k=1;k<j;k++){if(b[k]<b[j]){dp[i][j]=max(dp[i][j],dp[i][k]+1);}}}ans=max(ans,dp[i][j]);}}cout<<ans;return 0;
}

P2516 [HAOI2010] 最长公共子序列

d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共上升子序列, p a t h i , j path_{i,j} pathi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共上升子序列的方案数。

先处理出 d p i , j dp_{i,j} dpi,j。然后考虑如何处理 p a t h i , j path_{i,j} pathi,j

d p i , j = d p i − 1 , j dp_{i,j}=dp_{i-1,j} dpi,j=dpi1,j,说明是从 d p i − 1 , j dp_{i-1,j} dpi1,j 转移过来的,那么:

p a t h i , j = ( p a t h i , j + p a t h i − 1 , j ) m o d 10 8 path_{i,j}=(path_{i,j}+path_{i-1,j})\bmod 10^8 pathi,j=(pathi,j+pathi1,j)mod108

d p i , j = d p i , j − 1 dp_{i,j}=dp_{i,j-1} dpi,j=dpi,j1,则:

p a t h i , j = ( p a t h i , j + p a t h i , j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}+path_{i,j-1})\bmod 10^8 pathi,j=(pathi,j+pathi,j1)mod108

A i = B j A_i=B_j Ai=Bj d p i , j = d p i − 1 , j − 1 + 1 dp_{i,j}=dp_{i-1,j-1}+1 dpi,j=dpi1,j1+1,则:

p a t h i , j = ( p a t h i , j + p a t h i − 1 , j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}+path_{i-1,j-1})\bmod 10^8 pathi,j=(pathi,j+pathi1,j1)mod108

d p i − 1 , j − 1 = d p i , j dp_{i-1,j-1}=dp_{i,j} dpi1,j1=dpi,j,那么说明 d p i − 1 , j − 1 dp_{i-1,j-1} dpi1,j1 会重复计算,则:

p a t h i , j = ( p a t h i , j − p a t h i − 1 , j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}-path_{i-1,j-1})\bmod 10^8 pathi,j=(pathi,jpathi1,j1)mod108

实现

#include<bits/stdc++.h>
using namespace std;
const int mod=1e8;
string s,t;
int dp[5005][5005],n,m,path[5005][5005];
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>s>>t;n=s.size(),m=t.size();s=' '+s,t=' '+t;n--,m--;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}for(int i=0;i<=m;i++){path[0][i]=1;}for(int i=0;i<=n;i++){path[i][0]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(dp[i][j]==dp[i-1][j]){path[i][j]=(path[i][j]+path[i-1][j])%mod;}if(dp[i][j]==dp[i][j-1]){path[i][j]=(path[i][j]+path[i][j-1])%mod;}if(s[i]==t[j]&&dp[i][j]==dp[i-1][j-1]+1){path[i][j]=(path[i][j]+path[i-1][j-1])%mod;}if(dp[i-1][j-1]==dp[i][j]){path[i][j]=((path[i][j]-path[i-1][j-1])%mod+mod)%mod;}}}cout<<dp[n][m]<<'\n'<<path[n][m];return 0;
}

提交上去你就会发现是 70 70 70 分。

只有可怜的 125MB 空间显然过不了。

考虑滚动优化。

可以发现 p a t h path path 只涉及到 i − 1 i-1 i1 i i i 层,可以开两个数字分别处理第 i i i 层和第 i − 1 i-1 i1 层。

i − 1 i-1 i1 层用 p r e pre pre,进行存储。

然后:

d p i , j = d p i − 1 , j dp_{i,j}=dp_{i-1,j} dpi,j=dpi1,j,说明是从 d p i − 1 , j dp_{i-1,j} dpi1,j 转移过来的,那么:

p a t h j = ( p a t h j + p r e j ) m o d 10 8 path_j=(path_j+pre_j)\bmod 10^8 pathj=(pathj+prej)mod108

d p i , j = d p i , j − 1 dp_{i,j}=dp_{i,j-1} dpi,j=dpi,j1,则:

p a t h j = ( p a t h j + p a t h j − 1 ) m o d 10 8 path_j=(path_j+path_{j-1})\bmod 10^8 pathj=(pathj+pathj1)mod108

A i = B j A_i=B_j Ai=Bj d p i , j = d p i − 1 , j − 1 + 1 dp_{i,j}=dp_{i-1,j-1}+1 dpi,j=dpi1,j1+1,则:

p a t h j = ( p a t h j + p r e j − 1 ) m o d 10 8 path_j=(path_j+pre_{j-1})\bmod 10^8 pathj=(pathj+prej1)mod108

d p i − 1 , j − 1 = d p i , j dp_{i-1,j-1}=dp_{i,j} dpi1,j1=dpi,j,那么说明 d p i − 1 , j − 1 dp_{i-1,j-1} dpi1,j1 会重复计算,则:

p a t h i , j = ( p a t h i , j − p r e j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}-pre_{j-1})\bmod 10^8 pathi,j=(pathi,jprej1)mod108

初始化:

i ∈ [ 0 , m ] , p r e i = 1 i ∈ [ 0 , n ] , p a t h i = 1 i\in[0,m],pre_i=1\\ i\in[0,n],path_i=1 i[0,m],prei=1i[0,n],pathi=1

注意: p a t h path path 数组每一层都需要清空。

实现

#include<bits/stdc++.h>
using namespace std;
const int mod=1e8;
string s,t;
int dp[5005][5005],n,m,path[5005],pre[5005];
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>s>>t;n=s.size(),m=t.size();s=' '+s,t=' '+t;n--,m--;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}for(int i=0;i<=m;i++){pre[i]=1;}for(int i=0;i<=n;i++){path[i]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){path[j]=0;if(dp[i][j]==dp[i-1][j]){path[j]=(path[j]+pre[j])%mod;}if(dp[i][j]==dp[i][j-1]){path[j]=(path[j]+path[j-1])%mod;}if(s[i]==t[j]&&dp[i][j]==dp[i-1][j-1]+1){path[j]=(path[j]+pre[j-1])%mod;}if(dp[i-1][j-1]==dp[i][j]){path[j]=((path[j]-pre[j-1])%mod+mod)%mod;}}for(int j=1;j<=m;j++){pre[j]=path[j];}}cout<<dp[n][m]<<'\n'<<path[m];return 0;
}
```最长公共子序列问题(Longest Common Subsequence),该问题可以表述为,在 $A,B$ 中找出一段子序列 $x$,使得 $x$ 既是 $A$ 的子序列,又是 $B$ 的子序列。![](https://cdn.luogu.com.cn/upload/image_hosting/8c6poexo.png)你可以理解为,在两个序列中分别删除一些元素(剩下的不一定连续),使得两个序列的剩余部分相同且长度最长。暴力解法可以用 DFS,但是时间复杂度为 $O(|A|\times 2^{|B|})$,不能够满足我们的需求。考虑动态规划。定义:设 $dp_{i,j}$ 表示 $A$ 序列前 $i$ 个与 $B$ 序列的前 $j$ 个的最长公共子序列。答案为 $dp_{n,m}$。状态转移:$$
dp_{i,j}=\begin{cases}
dp_{i-1,j-1}& A_i=B_j\\
\max(dp_{i-1,j},dp_{i,j-1})&\text{otherwise}
\end{cases}
$$但如果需要输出任意合法路径(题目:[51Nod - 1006](https://vjudge.net/problem/51Nod-1006)),怎么办?递归倒推回去。设 $f(x,y)$ 表示 $A$ 序列前 $x$ 个和 $B$ 序列前 $y$ 个的匹配情况。如果 $A_x=B_y$,那么说明选择了这个点,输出 $A_x$(或 $B_y$),同时调用 $f(x-1,y-1)$。否则,如果 $dp_{x-1,y}>dp_{x,y-1}$,调用 $f(x-1,y)$。否则调用 $f(x,y-1)$。
### 实现
```cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[1005][1005];
string s,t;
void print(int x,int y){if(!x||!y){return;}if(s[x]==t[y]){print(x-1,y-1);cout<<s[x];}else{if(dp[x-1][y]>dp[x][y-1]){print(x-1,y);}else{print(x,y-1);}}
}
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>s>>t;s=' '+s,t=' '+t;for(int i=1;i<s.size();i++){for(int j=1;j<t.size();j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}print(s.size()-1,t.size()-1);return 0;
}

HDU-1159 Common Subsequence

最基础的那道题,本来应该放前面的。

d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共子序列。

答案为 d p n , m dp_{n,m} dpn,m

状态转移:

d p i , j = { d p i − 1 , j − 1 A i = B j max ⁡ ( d p i − 1 , j , d p i , j − 1 ) otherwise dp_{i,j}=\begin{cases} dp_{i-1,j-1}& A_i=B_j\\ \max(dp_{i-1,j},dp_{i,j-1})&\text{otherwise} \end{cases} dpi,j={dpi1,j1max(dpi1,j,dpi,j1)Ai=Bjotherwise

注意:*.size() 类型为无符号整型,不管在哪个容器都一样。

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[1005][1005]; // 设 dp[i][j] 表示 A 串前 i 个字符和 B 串前 j 个字符的最长公共子序列
string s,t;
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);while(cin>>s>>t){s=' '+s,t=' '+t;for(int i=1;i<s.size();i++){for(int j=1;j<t.size();j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}cout<<dp[s.size()-1][t.size()-1]<<'\n';}return 0;
}

U118717 最长公共上升子序列

注意:该做法不太严谨,数据太水,本来应该会 TLE 的。

d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共上升子序列。

答案: max ⁡ { d p i , j } \max\{dp_{i,j}\} max{dpi,j}

计算前先进行 d p i , j = d p i − 1 , j dp_{i,j}=dp_{i-1,j} dpi,j=dpi1,j

A i = B j A_i=B_j Ai=Bj,那么 d p i , j = max ⁡ ( d p [ i ] [ j ] , 1 ) dp_{i,j}=\max(dp[i][j],1) dpi,j=max(dp[i][j],1)

枚举 k ( k ∈ [ 1 , j − 1 ] ) k(k\in[1,j-1]) k(k[1,j1]),若 b k < b j b_k<b_j bk<bj,则:

d p i , j = max ⁡ ( d p i , j , d p i , k + 1 ) dp_{i,j}=\max(dp_{i,j},dp_{i,k}+1) dpi,j=max(dpi,j,dpi,k+1)

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,dp[3005][3005],a[3005],b[3005],ans;
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){cin>>b[i];}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){dp[i][j]=dp[i-1][j];if(a[i]==b[j]){dp[i][j]=max(dp[i-1][j],1ll);for(int k=1;k<j;k++){if(b[k]<b[j]){dp[i][j]=max(dp[i][j],dp[i][k]+1);}}}ans=max(ans,dp[i][j]);}}cout<<ans;return 0;
}

P2516 [HAOI2010] 最长公共子序列

d p i , j dp_{i,j} dpi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共上升子序列, p a t h i , j path_{i,j} pathi,j 表示 A A A 序列前 i i i 个与 B B B 序列的前 j j j 个的最长公共上升子序列的方案数。

先处理出 d p i , j dp_{i,j} dpi,j。然后考虑如何处理 p a t h i , j path_{i,j} pathi,j

d p i , j = d p i − 1 , j dp_{i,j}=dp_{i-1,j} dpi,j=dpi1,j,说明是从 d p i − 1 , j dp_{i-1,j} dpi1,j 转移过来的,那么:

p a t h i , j = ( p a t h i , j + p a t h i − 1 , j ) m o d 10 8 path_{i,j}=(path_{i,j}+path_{i-1,j})\bmod 10^8 pathi,j=(pathi,j+pathi1,j)mod108

d p i , j = d p i , j − 1 dp_{i,j}=dp_{i,j-1} dpi,j=dpi,j1,则:

p a t h i , j = ( p a t h i , j + p a t h i , j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}+path_{i,j-1})\bmod 10^8 pathi,j=(pathi,j+pathi,j1)mod108

A i = B j A_i=B_j Ai=Bj d p i , j = d p i − 1 , j − 1 + 1 dp_{i,j}=dp_{i-1,j-1}+1 dpi,j=dpi1,j1+1,则:

p a t h i , j = ( p a t h i , j + p a t h i − 1 , j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}+path_{i-1,j-1})\bmod 10^8 pathi,j=(pathi,j+pathi1,j1)mod108

d p i − 1 , j − 1 = d p i , j dp_{i-1,j-1}=dp_{i,j} dpi1,j1=dpi,j,那么说明 d p i − 1 , j − 1 dp_{i-1,j-1} dpi1,j1 会重复计算,则:

p a t h i , j = ( p a t h i , j − p a t h i − 1 , j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}-path_{i-1,j-1})\bmod 10^8 pathi,j=(pathi,jpathi1,j1)mod108

实现

#include<bits/stdc++.h>
using namespace std;
const int mod=1e8;
string s,t;
int dp[5005][5005],n,m,path[5005][5005];
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>s>>t;n=s.size(),m=t.size();s=' '+s,t=' '+t;n--,m--;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}for(int i=0;i<=m;i++){path[0][i]=1;}for(int i=0;i<=n;i++){path[i][0]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(dp[i][j]==dp[i-1][j]){path[i][j]=(path[i][j]+path[i-1][j])%mod;}if(dp[i][j]==dp[i][j-1]){path[i][j]=(path[i][j]+path[i][j-1])%mod;}if(s[i]==t[j]&&dp[i][j]==dp[i-1][j-1]+1){path[i][j]=(path[i][j]+path[i-1][j-1])%mod;}if(dp[i-1][j-1]==dp[i][j]){path[i][j]=((path[i][j]-path[i-1][j-1])%mod+mod)%mod;}}}cout<<dp[n][m]<<'\n'<<path[n][m];return 0;
}

提交上去你就会发现是 70 70 70 分。

只有可怜的 125MB 空间显然过不了。

考虑滚动优化。

可以发现 p a t h path path 只涉及到 i − 1 i-1 i1 i i i 层,可以开两个数字分别处理第 i i i 层和第 i − 1 i-1 i1 层。

i − 1 i-1 i1 层用 p r e pre pre,进行存储。

然后:

d p i , j = d p i − 1 , j dp_{i,j}=dp_{i-1,j} dpi,j=dpi1,j,说明是从 d p i − 1 , j dp_{i-1,j} dpi1,j 转移过来的,那么:

p a t h j = ( p a t h j + p r e j ) m o d 10 8 path_j=(path_j+pre_j)\bmod 10^8 pathj=(pathj+prej)mod108

d p i , j = d p i , j − 1 dp_{i,j}=dp_{i,j-1} dpi,j=dpi,j1,则:

p a t h j = ( p a t h j + p a t h j − 1 ) m o d 10 8 path_j=(path_j+path_{j-1})\bmod 10^8 pathj=(pathj+pathj1)mod108

A i = B j A_i=B_j Ai=Bj d p i , j = d p i − 1 , j − 1 + 1 dp_{i,j}=dp_{i-1,j-1}+1 dpi,j=dpi1,j1+1,则:

p a t h j = ( p a t h j + p r e j − 1 ) m o d 10 8 path_j=(path_j+pre_{j-1})\bmod 10^8 pathj=(pathj+prej1)mod108

d p i − 1 , j − 1 = d p i , j dp_{i-1,j-1}=dp_{i,j} dpi1,j1=dpi,j,那么说明 d p i − 1 , j − 1 dp_{i-1,j-1} dpi1,j1 会重复计算,则:

p a t h i , j = ( p a t h i , j − p r e j − 1 ) m o d 10 8 path_{i,j}=(path_{i,j}-pre_{j-1})\bmod 10^8 pathi,j=(pathi,jprej1)mod108

初始化:

i ∈ [ 0 , m ] , p r e i = 1 i ∈ [ 0 , n ] , p a t h i = 1 i\in[0,m],pre_i=1\\ i\in[0,n],path_i=1 i[0,m],prei=1i[0,n],pathi=1

注意: p a t h path path 数组每一层都需要清空。

实现

#include<bits/stdc++.h>
using namespace std;
const int mod=1e8;
string s,t;
int dp[5005][5005],n,m,path[5005],pre[5005];
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>s>>t;n=s.size(),m=t.size();s=' '+s,t=' '+t;n--,m--;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(s[i]==t[j]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}for(int i=0;i<=m;i++){pre[i]=1;}for(int i=0;i<=n;i++){path[i]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){path[j]=0;if(dp[i][j]==dp[i-1][j]){path[j]=(path[j]+pre[j])%mod;}if(dp[i][j]==dp[i][j-1]){path[j]=(path[j]+path[j-1])%mod;}if(s[i]==t[j]&&dp[i][j]==dp[i-1][j-1]+1){path[j]=(path[j]+pre[j-1])%mod;}if(dp[i-1][j-1]==dp[i][j]){path[j]=((path[j]-pre[j-1])%mod+mod)%mod;}}for(int j=1;j<=m;j++){pre[j]=path[j];}}cout<<dp[n][m]<<'\n'<<path[m];return 0;
}

相关文章:

  • CppCon 2014 学习:Practical Functional Programming
  • MCP架构深度解析:从基础原理到核心设计
  • ROS2与Unitree机器人集成指南
  • 【Java基础05】面向对象01
  • python中常用的内置属性built-in attributes
  • 《高等数学》(同济大学·第7版) 的 详细章节目录
  • 10.安卓逆向2-frida hook技术-frida基本使用-frida指令(用于hook)
  • DeepSeek API流式输出与多轮对话示例
  • DFS入门刷题
  • OD 算法题 B卷【模拟消息队列】
  • Arm处理器调试采用jlink硬件调试器的命令使用大全
  • 人工智能在智能供应链中的创新应用与未来趋势
  • 2024年ESWA SCI1区TOP,自适应学习灰狼算法ALGWO+无线传感器网络覆盖优化,深度解析+性能实测
  • 开源版 PyMOL 如何绘制 Galidesivir 分子结构 ?
  • 项目:贪吃蛇实现
  • 流媒体协议分析:流媒体传输的基石
  • Jinja2 模板继承机制
  • Mybatis ORM SpringORM
  • 【代码坏味道】膨胀类 Bloaters
  • Go语言的context
  • 成都微信网站开发/广告设计需要学什么
  • 肇庆住房建设部网站/全网营销代运营
  • 如何查看网站是否被k/百度指数平台
  • 怎样给公司做免费网站/cps推广平台
  • 二维码生成器使用方法/seo手机关键词网址
  • 怎么做网站的投票平台/1000个关键词