题解 SP9098【LCS3 - Long Common Subsequence】
题意
给你长度为 $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~

浙公网安备 33010602011771号