子序列自动机瞎吹

题面 :

多组测试数据,给出一个串 S 多次查询 T 是不是 S 的子序列。

  • Step 1: 我们如果想要在一个串 S 里面,去查询一个子序列 T, 那么可以怎么做?

Solution 1:

考虑暴力双指针 (i,j) ,当我们发现一个 S 中的一个新的最近的位置 j 使得 (i+1,j) 是可以匹配的,那么我们就暴力移动他这个是 O(n+m) 的,其中 m,n 分别是母串和子序列的长度。总时间是 O(qm+n) 的.

Solution 2:

考虑优化暴力的过程我们可以直接记录 Nxti,j 表示位置 i 后下一个j 位置。(这个是我在做积木小赛的时候发现的),我们可以用它来 O(n) 匹配。

这就是子序列自动机,我们可以用它做到 O(kn+ms) 来匹配,其中 k 是单次往下走的时间,s 是字符集的长度。

  • Step 2: Compare

这两个东西看起来差不多快啊 (退钱!),但是,我们不妨钦定 mn2 级别的,然后字符集是几乎为线性的一个常数。可以解得 : 在算法 1 下的 n 可以达到 103级别,但不能维护多的询问,而算法 2 下处理却是游刃有余。

但是,如果没有字符集的限制,我们该如何去处理呢?
首先,观察最基础的处理方式,因为每次至多更新 1 个字符,所以 Nxt 至多更新一个元素,而我们则浪费了太多的时间在维护不变的指针上面。
然后,我们发现,这个指针是在上一个版本上建立的(要素察觉),所以我们需要维护一个支持快速维护数组的增量数据结构,也就是可持久化数组,故我们可以使用一颗可持久化线段树去维护子序列自动机。

故我们有 O(nlogm+mlogm) 的时间解决该问题,当然,我们也有更简单的解决方式,我们可以拿一个 vector 存储所有元素的下标,然后二分。

#include<bits/stdc++.h>
#define mid (l + r) / 2
using namespace std;
const int MN = 1e6 + 5;
inline int read() {
	int x = 0; bool f = 0; char c = getchar();
	while(c < '0' || c > '9') (c == '-') ? f = 1, c = getchar() : c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
	return (f) ? -x : x;
}
int n, q, lim, cnt, nxt, l, opt, a[MN], b[MN] , rt[MN], ls[MN * 80], rs[MN * 80], val[MN * 80];
inline void Build(int & u, int l, int r) {
	u = ++ cnt;
	if (l == r) {val[u] = 0; return ;}
	Build(ls[u], l, mid), Build(rs[u], mid + 1, r);
	return ;
}
inline void Modify(int pre, int & u, int l, int r, int pos, int k) {
	u = ++ cnt;
	if (l == r) {val[u] = k; return ;}
	if (pos <= mid) rs[u] = rs[pre], Modify(ls[pre], ls[u], l, mid, pos, k);
	else ls[u] = ls[pre], Modify(rs[pre], rs[u], mid + 1, r, pos, k);
}
inline int Qur(int u, int l, int r, int pos) {
	if (l == r) {return val[u];}
	if (pos <= mid) return Qur(ls[u], l, mid, pos);
	else return Qur(rs[u], mid + 1, r, pos);
}
//主席树板子
int main () {
    opt = read(), n = read(), q = read(), lim = read(), Build(rt[n + 1], 1, n);
    for (int i = 1; i <= n; i ++) a[i] = read(); //读入
    for (int i = n; i >= 1; i --) Modify(rt[i + 1], rt[i], 1, n, a[i], i); //维护子序列自动机
    for (int i = 1; i <= q; i ++) {
    	l = read(), nxt = 1; for (int j = 1; j <= l; j ++) b[j] = read();
    	for (int j = 1; j <= l; j ++) {
    		nxt = Qur(rt[nxt], 1, n, b[j]); // 查询下一个数位置
    		if (! nxt) {
    			printf("No\n"); break ;
			}
			nxt ++; //跳到这之后的位置
		}
		if (nxt) printf("Yes\n");
	}
	return 0;
}

posted @   Cust10  阅读(118)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示