子序列自动机瞎吹
题面 :
多组测试数据,给出一个串 S 多次查询 T 是不是 S 的子序列。
- Step 1: 我们如果想要在一个串 S 里面,去查询一个子序列 T, 那么可以怎么做?
Solution 1:
考虑暴力双指针 ,当我们发现一个 中的一个新的最近的位置 使得 是可以匹配的,那么我们就暴力移动他这个是 的,其中 分别是母串和子序列的长度。总时间是 的.
Solution 2:
考虑优化暴力的过程我们可以直接记录 表示位置 后下一个 位置。(这个是我在做积木小赛的时候发现的),我们可以用它来 匹配。
这就是子序列自动机,我们可以用它做到 来匹配,其中 是单次往下走的时间, 是字符集的长度。
- Step 2: Compare
这两个东西看起来差不多快啊 (退钱!),但是,我们不妨钦定 是 级别的,然后字符集是几乎为线性的一个常数。可以解得 : 在算法 1 下的 可以达到 级别,但不能维护多的询问,而算法 2 下处理却是游刃有余。
但是,如果没有字符集的限制,我们该如何去处理呢?
首先,观察最基础的处理方式,因为每次至多更新 1 个字符,所以 至多更新一个元素,而我们则浪费了太多的时间在维护不变的指针上面。
然后,我们发现,这个指针是在上一个版本上建立的(要素察觉),所以我们需要维护一个支持快速维护数组的增量数据结构,也就是可持久化数组,故我们可以使用一颗可持久化线段树去维护子序列自动机。
故我们有 的时间解决该问题,当然,我们也有更简单的解决方式,我们可以拿一个 存储所有元素的下标,然后二分。
#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;
}
登高自卑,行远自迩。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话