子序列自动机
作用
判断一个串 \(B\) 是否为串 \(A\) 的一个子序列。
构建
子序列自动机的核心就是 \(to_{i,c}\),表示在 \(A[i+1,|A|]\) 中,\(c\) 第一次出现的位置。
考虑怎么建立?可以对每个字符开一个 vector,里面存对应字符出现的下标位置。
每次查询 \(to_{i,c}\) 就在 vector 中二分即可。
构建复杂度 \(O(|A|)\),查询复杂度 \(O(\log|A|)\)。
还有另一种构建复杂度 \(O(|A||\sum|)\),查询复杂度 \(O(1)\) 的做法,也就是直接维护 \(to_{i,c}\) 这个数组,这是简单的就不再赘述。
代码
struct SQAM {
vector< int> vec[N];
void build( int a[], int n) {
for ( int i = 1; a[i]; i ++) vec[a[i]].push_back(i);
for ( int i = 1; i <= n; i ++) vec[i].push_back(inf);// 最后插入一个极大值,就可以表示查询不到对应节点
}
int ask( int u, int c) {
return * upper_bound(vec[c].begin(), vec[c].end(), u);
}
} T;
【模板】子序列自动机
那么如何判断 \(B\) 是否为 \(A\) 的子序列?
开一个变量 \(now\),表示当前所在的 \(A\) 的节点。
顺序遍历 \(B\),让 \(now\leftarrow to_{now,B_i}\),如果最后 \(now\) 不为极大值,也就是能找到对应节点,那么 \(B\) 就为 \(A\) 的子序列,反之就不是。
#include <bits/stdc++.h>
void Freopen() {
freopen("", "r", stdin);
freopen("", "w", stdout);
}
using namespace std;
const int N = 2e5 + 10, M = 2e5 + 10, inf = 1e9, mod = 998244353;
int tp, n, q, m;
int a[N];
struct SQAM {
vector< int> vec[N];
void build( int a[], int n) {
for ( int i = 1; a[i]; i ++) vec[a[i]].push_back(i);
for ( int i = 1; i <= n; i ++) vec[i].push_back(inf);
}
int ask( int u, int c) {
return * upper_bound(vec[c].begin(), vec[c].end(), u);
}
} T;
signed main() {
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> tp >> n >> q >> m;
for ( int i = 1; i <= n; i ++) cin >> a[i];
T.build(a, m);
while (q --) {
int l; cin >> l;
int now = 0;
while (l --) {
int x; cin >> x;
if (now != inf) now = T.ask(now, x);
}
if (now == inf) cout << "No\n";
else cout << "Yes\n";
}
return 0;
}

浙公网安备 33010602011771号