P5826 【模板】子序列自动机

题目链接

P5826 【模板】子序列自动机

【模板】子序列自动机

题目背景

本题中,若 xy 的子序列,则等价于存在一个单调递增序列 z,满足 |z|=|x|z|x||y|,且 i[1, |x|], yzi=xi。其中 |x|, |y|, |z| 分别代表序列 x, y, z 的长度,xi, yi, zi 分别代表序列 x, y, z 的第 i 项。

这是一道在 yLOI2020 备选题里被毙掉的题目。

题目描述

给定一个长度为 n 的正整数序列 a ,有 q 次询问,第 i 次询问给定一个长度为 Li 的序列 bi,请你判断 bi 是不是 a 的子序列。序列 a 和所有 bi 中的元素都不大于一个给定的正整数 m

输入格式

每个测试点有且仅有一组数据。

输入的第一行是四个用空格隔开的整数,分别代表 type, n, q, m。其中 type 代表测试点所在的子任务编号,其余变量的含义见【题目描述】。

输入的第二行是 n 个用空格隔开的整数,第 i 个数字代表序列 a 的第 i 个元素 ai

3 行至第 (q+2) 行,每行代表一次询问。第 (i+2) 行的输入格式为:

  • (i+2) 行的行首有一个整数 li,代表第 i 次询问的序列长度。一个空格后有 li 个用空格隔开的整数。该行的第 (j+1) 个整数代表序列 bi 的第 j 个元素 bi,j

输出格式

对于每次询问,输出一行一个字符串,若给定的序列是 a 的子序列,则输出 Yes,否则输出 No

样例 #1

样例输入 #1

0 5 5 5 1 3 2 2 4 3 1 5 2 2 3 2 3 1 2 3 3 1 2 4 5 1 3 2 2 4

样例输出 #1

No Yes No Yes Yes

提示

样例 1 解释

  • 对于第一次询问,原序列中没有 5,所以给定序列显然不是原序列的子序列。
  • 对于第二次询问,存在两个合法的序列 z,分别是 {2, 3}{2, 4}。即 a2=b2,1, a3=b2,2a2=b2,1, a4=b2,2。这里 bi,j 代表序列 bi 的第 j 个元素,序列 z 的含义见【题目背景】,下同。
  • 对于第三次询问,不存在合法的序列 z
  • 对于第四次询问,存在两个合法的序列 z,分别是 {1, 3, 5}{1, 4, 5}
  • 对于第五次询问,存在一个合法的序列 z,为 {1, 2, 3, 4, 5}

数据范围与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(20 points):type=1n,q,m100i=1qli103
  • Subtask 2(35 points):type=2n,q105m26i=1qli106
  • Subtask 3(45 points):type=3n,q,m105i=1qLi106

对于全部的测试点,保证 1n,m,q1051ai,bi,jm1li106i=1qli106

提示

  • 请注意常数因子对程序效率造成的影响。

  • 本题输入规模较大,请注意数据读入对程序效率造成的影响。

  • 请注意输入第一行的顺序为先输入询问次数 q,再输入序列元素最大值 m

解题思路

序列自动机

考虑暴力的情况:nxt[i][j] 表示当前从母串 i 开始,匹配 匹配串 j 这个字符的第一次出现的位置,从后往前更新 nxt[i][j] 即可,而且 nxt[i]nxt[i+1] 只有一个 j 的位置不同,这就启发可以用主席树维护这些信息,即建立 n 棵线段树,每次修改时只会在一处位置发生变化,即值域 a[i] 这个位置要变为 i,同时主席树的历史根节点编号即 i 这个下标,查询时只要查询对应当前根节点表示的线段树中是否存在对应值对应的位置

  • 时间复杂度:O(n+i=1qli)×logn)

二分

a 的下标用桶存下来,每次读入子序列时,都从当前值满足条件的最前面开始搜索,如果搜不到则一定不是子序列

  • 时间复杂度:O(i=1qli×logn)

代码

  • 序列自动机
// Problem: P5826 【模板】子序列自动机 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P5826 // Memory Limit: 500 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=5e5+5,M=N*4+17*N; int a[N],root[N],idx; struct Tr { int l,r,v; }tr[M]; int build(int l,int r) { int p=++idx; if(l==r)return p; int mid=l+r>>1; tr[p].l=build(l,mid),tr[p].r=build(mid+1,r); return p; } int insert(int p,int l,int r,int x,int v) { int q=++idx; tr[q]=tr[p]; if(l==r) { tr[q].v=v; return q; } int mid=l+r>>1; if(x<=mid)tr[q].l=insert(tr[p].l,l,mid,x,v); else tr[q].r=insert(tr[p].r,mid+1,r,x,v); return q; } int ask(int p,int l,int r,int x) { if(l==r)return tr[p].v; int mid=l+r>>1; if(x<=mid)return ask(tr[p].l,l,mid,x); return ask(tr[p].r,mid+1,r,x); } int main() { int t,n,q,m,x,l; scanf("%d%d%d%d",&t,&n,&q,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); root[n+1]=build(1,m); for(int i=n;i;i--)root[i]=insert(root[i+1],1,m,a[i],i); while(q--) { bool res=true; int nxt=1; for(scanf("%d",&l);l;l--) { scanf("%d",&x); nxt=ask(root[nxt],1,m,x); if(!nxt)res=false; else nxt++; } puts(res?"Yes":"No"); } return 0; }
  • 二分
// Problem: P5826 【模板】子序列自动机 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P5826 // Memory Limit: 500 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e5+5; vector<int> a[N]; int main() { int t,n,q,m,x,l; scanf("%d%d%d%d",&t,&n,&q,&m); for(int i=1;i<=n;i++) { scanf("%d",&x); a[x].pb(i); } while(q--) { bool res=true; int pos=0; for(scanf("%d",&l);l;l--) { scanf("%d",&x); pos=upper_bound(a[x].begin(),a[x].end(),pos)-a[x].begin(); if(pos<a[x].size())pos=a[x][pos]; else res=false; } puts(res?"Yes":"No"); } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16839350.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示