题解:CF346B Lucky Common Subsequence
题目大意
询问一个最长子序列 \(S\) 满足,是 \(A\) 串和 \(B\) 串的公共子序列且 \(C\) 串不是 \(S\) 的子串。
思路
考虑动态规划,定义状态 \(dp_{i,j,k}\) 为:跟 \(A\) 串匹配了前 \(i\) 位,跟 \(B\) 串匹配了前 \(j\) 位的最长子序列与 \(C\) 串匹配了 \(k\) 位。考虑转移,如果 \(A_i\) 与 \(B_j\) 不相等的话,那么转移式为:
\[dp_{i,j,k}=\max(dp_{i,j,k},dp_{i-1,j,k},dp_{i,j-1,k})
\]
如果这两位相等的话,那么因为子序列改变了,所以与 \(C\) 串的匹配长度也要改变,那么需要重新匹配,注意到,这里的重新匹配类似于 \(KMP\) 里的失配后的操作,所以我们先对 \(C\) 串求出失配数组 next,然后每次匹配失败的时候,直接跳到 next 数组里对应的位置即可,假设重新匹配后与 \(C\) 串的匹配长度为 \(t\),那么转移式为:
\[dp_{i,j,k}=\max(dp_{i,j,k},dp_{i-1,j-1,t}+A_i)
\]
注意一下,我这里的 \(dp_{i,j,k}\) 是 \(string\) 类型的,所以转移时就比较 \(size\) 大小。
如果 \(C\) 串的大小为 \(k\),最后答案是 \(\min (dp_{n,m,i})(i \in [0,k))\)。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=110;
ll nxt[N];
string s,t,g,dp[N][N][N],ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>s>>t>>g;
ll lens=s.size(),lent=t.size(),leng=g.size(),j=0;
for(int i=1;i<leng;i++){
while(j>0&&g[i]!=g[j])j=nxt[j-1];
if(g[i]==g[j])nxt[i]=++j;
}
for(int i=1;i<=lens;i++){
for(int j=1;j<=lent;j++){
for(int k=0;k<leng;k++){
if(s[i-1]==t[j-1]){
char p=s[i-1];
ll len=k;
while(len>0&&p!=g[len])len=nxt[len-1];
if(p==g[len])len++;
if(dp[i][j][len].size()<dp[i-1][j-1][k].size()+1){
dp[i][j][len]=dp[i-1][j-1][k]+p;
}
}
if(dp[i][j][k].size()<dp[i-1][j][k].size())dp[i][j][k]=dp[i-1][j][k];
if(dp[i][j][k].size()<dp[i][j-1][k].size())dp[i][j][k]=dp[i][j-1][k];
}
}
}
for(int i=0;i<leng;i++){
if(ans.size()<dp[lens][lent][i].size()){
ans=dp[lens][lent][i];
}
}
if(!ans.size())cout<<0;
else cout<<ans;
return 0;
}

浙公网安备 33010602011771号