模板:子序列自动机(字符串)

所谓子序列自动机,就是根据子序列建立的自动机。

(逃)

前言

小清新算法。

解析

和其他自动机类似的,我们希望子序列自动机能且只能接受原串的所有子序列。

考虑一个问题:给你一个串 T,如何判断它是否是原串 S 的子串?
一个经典的贪心策略是:设当前在 T 的匹配位置是 i,S 的匹配位置是 j,那么就找到 S 的位置 j 之后第一个 T[i+1] 出现的位置 p,然后令 i=i+1,j=p,直到T全部匹配或S失配为止。

把类似的思想迁移到子序列自动机上,设 t o i , j to_{i,j} toi,j 表示 i 之后 第一个 j 出现的位置,匹配的时候不断跳 to 就行了,to数组可以通过倒着扫一边求解。

字符集较小时直接暴力为 to 数组赋值即可,字符集较大的时候考虑到 t o i to_i toi t o i + 1 to_{i+1} toi+1 只有一位不同,所以使用可持久化线段树即可,复杂度可能要多一个 log。

代码

P5826 【模板】子序列自动机

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ULL unsigned ll
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read() {
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=1e5+100;
const int mod=998244353;
const ll inf=1e9;

bool mem1;

bool Flag=0;

int n,m,q;

struct node{
	int ls,rs,w;
}tr[N*20];
int rt[N],tot;
inline int copy(int x){
	tr[++tot]=tr[x];
	return tot;
}
#define mid ((l+r)>>1)
void upd(int &k,int l,int r,int p,int w){
	k=copy(k);
	if(l==r){
		tr[k].w=w;
		return;
	}
	if(p<=mid) upd(tr[k].ls,l,mid,p,w);
	else upd(tr[k].rs,mid+1,r,p,w);
	//printf("  k=%d (%d %d) ls=%d rs=%d\n",k,l,r,tr[k].ls,tr[k].rs);
}
int ask(int k,int l,int r,int p){
	//printf("  k=%d (%d %d) ls=%d rs=%d\n",k,l,r,tr[k].ls,tr[k].rs);
	if(!k) return n+1;
	if(l==r) return tr[k].w;
	if(p<=mid) return ask(tr[k].ls,l,mid,p);
	else return ask(tr[k].rs,mid+1,r,p);
}
int a[N];

int main(){
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif
	read();
	n=read();q=read();m=read();
	tr[0].w=n+1;
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=n-1;i>=0;i--){
		//printf("i=%d\n",i);
		rt[i]=rt[i+1];
		upd(rt[i],1,m,a[i+1],i+1);
	}
	for(int t=1;t<=q;t++){
		int len=read();
		for(int i=1;i<=len;i++) a[i]=read();
		int p=0;
		for(int i=1;i<=len&&p<=n;i++){
			p=ask(rt[p],1,m,a[i]);
			//printf("  i=%d a=%d p=%d\n",i,a[i],p);
		}
		if(p<=n) puts("Yes");
		else puts("No");
	}
	return 0;
}
/*
*/
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值