题解 SP9098【LCS3 - Long Common Subsequence】

$\text{Link}$

题意

给你长度为 $n$ 的序列 $a$ 和 $q$ 次询问,每次询问给出 $m$ 和长度为 $m$ 的序列 $b$,求出这两个序列的最长公共子序列,要求字典序最小。

$n\le10^6,0\le a_i,b_i\le10^4,q\le5\times 10^3,m\le5$。

思路

啪的一下就想到子序列自动机了很快啊!

一开始的思路是枚举 $b$ 序列中 $\text{LCS}$ 的起点然后丢进子序列自动机中匹配,若能匹配上就放入答案中。

然后发现这是错误的贪心,于是开始想其他的做法,但我只会 $\text{dfs}$ 了。

设计 $\text{dfs}(cur,last,now)$ 分别表示当前匹配到的节点、上个被匹配的位置、现在选择的集合。

然后可以有 $\text{dfs}(cur,last,now)\to\text{dfs}(cur+1,last,now)$,若 $b_{cur}$ 与 $a_{match}$ 匹配了则还有 $\text{dfs}(cur,last,now)\to \text{dfs}(cur+1,match,now\cup b_{cur})$。

注意要取字典序最小,翻译没说到。这个在搜的时候判一下就好了。

使用 vector 构建子序列自动机,时间复杂度 $O(n+q2^m(m+\log n))\approx5\times10^6$,空间复杂度 $O(n+m^2)$,可以通过。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e5+10;
vector<int>p[N],ans;
int a[N],n,q,m;
inline int findnxt(int c,int last){
    last=upper_bound(p[c].begin(),p[c].end(),last)-p[c].begin();
    if(last>=p[c].size())
        return -1;
    return p[c][last];
}
inline bool cmp(vector<int>a,vector<int>b){
    if(a.size()!=b.size()) return a.size()>b.size();
    for(int i=0;i<a.size();i++){
        if(a[i]!=b[i]) return a[i]<b[i];
    }
}
inline void dfs(int cur,int last,vector<int>now){
    if(cur==m+1) return ;
    dfs(cur+1,last,now);
    int i=findnxt(a[cur],last);
    if(~i){
        now.push_back(a[cur]);
        if(cmp(now,ans)) ans=now;
        dfs(cur+1,i,now);
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        p[read()].push_back(i);
    }
    q=read();
    while(q--){
        int last=0;
        m=read();
        for(int i=1;i<=m;i++){
            a[i]=read();
        }
        ans.clear();
        dfs(1,0,vector<int>());
        write(ans.size());
        putc(' ');
        for(int i=0;i<ans.size();i++){
            write(ans[i]),putc(' ');
        }
        putc('\n');
    }
    flush();
}

再见 qwq~

posted @ 2021-07-03 10:34  ffffyc  阅读(6)  评论(0)    收藏  举报  来源