P5826 【模板】子序列自动机

不会用STL的路过

题目大意

给出一个序列 a a a 和一些序列,需要判断这些序列是否是 a a a 的字序列.

分析

这篇文章不会用到vector,保证可以让pascal等语言的用户也看懂并且可以轻松实现.

序列自动机

如何快速判断一个序列是否是另一个序列的子序列,这时就可以用到序列自动机了,对于暴力的做法可以枚举需要查询的序列的第 i i i 个位置,在用一个类似指针的东西枚举在原序列中的下一个相等的位置,可以发现这样的时间复杂度为 O ( ∣ a ∣ ) \mathcal{O}(|a|) O(a)( ∣ a ∣ |a| a表示原序列的长度,末日查询序列长度小于原序列长度),而序列自动机就是讲每个位置的下一个出现元素的位置全部保存,这样就可以做到 O ( 1 ) \mathcal{O}(1) O(1) 查询下一个出现的位置了,但是这个东西的的预处理的时间复杂度为 O ( \mathcal{O}( O( 值域大小 × ∣ a ∣ ) \times |a|) ×a),显然会MLE,那么可以发现对于相邻的两个数的这个查询下一个位置的数组最多只有一个位置不一样,所以,就是可以持久化线段树就可以解决了.

最后的时间复杂度为 O ( ∑ i = 1 q L i log ⁡ 2 m + n log ⁡ 2 m ) \mathcal{O}(\sum_{i=1}^qL_i\log_2m+n\log_2m) O(i=1qLilog2m+nlog2m).

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int maxN=1e6+7;
int N,M;
int top=0;
int a[maxN],b[maxN];
struct Tree//主席树模板,不多讲了
{
	int lson,rson,sum;
}tree[maxN*80];
int point_cnt=0;
#define LSON tree[now].lson
#define RSON tree[now].rson
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
#define NEW_LSON tree[new_tree].lson
#define NEW_RSON tree[new_tree].rson
void Updata(int num,int change,int &new_tree,int now,int left=1,int right=top)
{
	if(num<left||right<num)
	{
		new_tree=now;
		return;
	}
	new_tree=++point_cnt;
	tree[new_tree].lson=0;
	tree[new_tree].rson=0;
	tree[new_tree].sum=0;
	if(left==right)
	{
		tree[new_tree].sum=change;
		return;
	}
	if(MIDDLE>=num)
	Updata(num,change,NEW_LSON,LEFT);
	else
	NEW_LSON=LSON;
	if(MIDDLE+1<=num)
	Updata(num,change,NEW_RSON,RIGHT);
	else
	NEW_RSON=RSON;
}
int Query(int num,int now,int left=1,int right=top)
{
	if(num<left||right<num)
	{
		return 0;
	}
	if(left==right)
	{
		return tree[now].sum;
	}
	return Query(num,LEFT)+Query(num,RIGHT);
}
int root[maxN];
void work()//对于每一个序列
{
    point_cnt=0;
	scanf("%d",&M);
	int now=1;
	REP(i,1,M)
	{
		scanf("%d",&b[i]);
	}
	REP(i,1,M)
	{
		now=Query(b[i],root[now]);//查询这个数下一个出现的位置
		if(now==0)//如果不存在就是No
		{
			printf("No\n");
			return;
		}
		now++;//这里记录的是包含自己的所以需要++
	}
	printf("Yes\n");//存在
}
int main()
{
    int opt;
	int T;
	scanf("%d%d%d%d",&opt,&T,&N,&top);
	REP(i,1,N)
	{
		scanf("%d",&a[i]);
	}
	DOW(i,N,1)//倒叙预处理
	{
		Updata(a[i],i,root[i],root[i+1]);
	}
	REP(i,1,T)
	{
		work();
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值