子序列自动机

作用

判断一个串 \(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;
}
posted @ 2025-08-19 17:09  咚咚的锵  阅读(29)  评论(0)    收藏  举报