2020.03.01日常总结兼子序列自动机略讲

【 算 法 讲 解 】 \color{green}{【算法讲解】}

【 模 板 链 接 】 : \color{blue}{【模板链接】:} 洛谷P5826 【模板】子序列自动机

【 算 法 目 标 】 : \color{blue}{【算法目标】:}
在这里插入图片描述
在这里插入图片描述
【 算 法 略 讲 】 : \color{blue}{【算法略讲】:} 首先,我们来看看暴力如何完成这道题。很简单,拿一个指针,从头扫描到尾,单次查找时间复杂度 O ( n ) O(n) O(n)

回归正题,什么是子序列自动机?很简单,就是一个序列而已。记 p o s x pos_x posx保存数字 x x x的位置。比如序列为 [ 1 , 2 , 1 , 5 , 5 ] [1,2,1,5,5] [1,2,1,5,5],则 p o s 1 = [ 1 , 3 ] pos_1=[1,3] pos1=[1,3]


这个东西有什么用呢?我们可以用它来优化查找的过程。什么意思呢?比如我们现在找到了 l s t lst lst这么个位置上,我们想找一个数字 5 5 5,我们就只需找到 p o s 5 pos_5 pos5 > l s t >lst >lst的一个位置即可。

等等,如果有很多位置的话,我们找哪个呢?很简单,找第一个,因为它留给后面匹配的空间最大,最容易满足条件。

因为 p o s pos pos是递增的,所以这一过程我们可以用二分实现,总时间复杂度 O ( n + ( ∑ i = 1 q L i ) × log ⁡ m ) O(n+(\sum\limits_{i=1}^{q} L_i)\times \log m) O(n+(i=1qLi)×logm)

还有,直接开数组会MLE哦,怎么办?直接用一个vector即可。

【 代 码 】 : \color{blue}{【代码】:}

#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
const int N=1e5+100;
vector<int> pos[N];
vector<int>::iterator t;
int n,q,m,a[N],lst;bool sign;
int main(){
	read();n=read();q=read();read();
	for(int i=1;i<=n;i++)
		pos[read()].push_back(i);//计算pos数组
	for(int test=1;test<=q;test++){
		m=read();sign=true;lst=0;
		for(int i=1;i<=m;i++)
			a[i]=read();//输入待匹配数组
		for(int i=1;i<=m;i++){
			t=upper_bound(pos[a[i]].begin(),pos[a[i]].end(),lst);
			if (t==pos[a[i]].end()){sign=false;break;}
			lst=*t;//指针t对应的位置就是lst的新值
		}
		puts(sign?"Yes":"No");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值