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

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;
}
http://www.xdnf.cn/news/10486.html

相关文章:

  • Java核心:Object与Objects方法全解析
  • VAE在扩散模型中的技术实现与应用
  • 【代码坏味道】无用物Dispensables
  • 【Qt】EventFilter,要增加事件拦截器才能拦截到事件
  • CppCon 2014 学习:Practical Functional Programming
  • 给跑步入门的一个训练课表
  • RAGFlow从理论到实战的检索增强生成指南
  • Excel如何去除公式保留数值
  • 知识管理五强对比:Baklib高效突围
  • 10000+套PPT模版合集和简历模版 【多种系列风格】免费下载
  • Python 全面技术指南:从语言本质到工程实践
  • 第六十三节:深度学习-模型推理与后处理
  • 流媒体协议分析:流媒体传输的基石
  • MCP架构全解析:从核心原理到企业级实践
  • java开发中#和$的区别
  • 「 扑翼飞行器 」悬停飞行的信号串联滤波器设计
  • leetcode hot100刷题日记——31.二叉树的直径
  • 【计算机CPU架构】ARM架构简介
  • 差分隐私-扰动机制
  • Redis 常用数据类型和命令使用
  • Go语言的原子操作
  • 告别压降损耗与反向电流困扰:汽车电子电源防反接方案全面解析与理想二极管应用
  • 桥 接 模 式
  • 导入Maven项目
  • 人工智能在智能供应链中的创新应用与未来趋势
  • 数的划分--dfs+剪枝
  • 配置前端控制器
  • 【Hot 100】121. 买卖股票的最佳时机
  • 【优比】基于STM32的紧急求助定位导盲仪系统
  • python打卡训练营打卡记录day41