题解: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;
}
posted @ 2025-09-10 21:36  一班的hoko  阅读(9)  评论(0)    收藏  举报