• 【数据结构】子序列自动机


    子序列自动机

    这东西是我刷 ARC 的时候遇到的,慕名而来

    结合模板题阅读:

    https://www.luogu.com.cn/problem/P5826

    构建

    这个自动机原理十分简单,你可以将它当作一个 dp 来食用:

    记所给的字符串为 \(w\),字符集为 \(S\)\(next[i][ch]\) 为第 \(i\) 个字符之后(不包括位置 \(i\))字符 \(ch\) 所在的最近的位置。

    那么我们有转移方程:

    \[next[i][j] = next[i+1][j] ~~~~~~~ (w[i+1] \ne j) \\ next[i][j] = i+1 ~~~~~~~ (w[i+1] = j) \]

    注意到转移的方向,我们只需要倒序扫一遍字符串并构建 \(next\) 即可。

    优化

    当字符集 \(S\) 大小 \(|S|\) 很小的时候,直接转移就够了。

    那,\(|S|\) 比较大的时候如何处理呢?

    注意到对于上面的转移方程,其实只有字符 \(j = w[i+1]\) 的时候 \(next[i][j]\) 才会被更新,那么我们不妨将 \(next[i]\) 看成是一个桶(值域是字符集),则我们需要做的操作就是在桶的位置 \(j\) 作单点修改。

    而因为我们需要开 \(n\)(字符串 \(w\) 长度)次桶,每次都相应地作单点修改,因此这一过程可以用主席树来维护。

    查询是否存在所求子序列

    \(next\) 构建过程知这是基于贪心的思想构建的,所以我们将待查询的串 \(str\) 扔到 \(next\) 上并根据查询串的字符作转移,如果跳出去了就肯定没有,反之必然有,复杂度 \(O(|str|)\)

    也就是本模板题,代码见下面的实现

    实现

    截至目前,洛谷最优解有五页,我排到第四页,QAQ

    #include<bits/stdc++.h>
    using namespace std;
    
    #define debug(x) cerr << #x << ": " << (x) << endl
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define pb push_back
    #define all(x) (x).begin(), (x).end()
    
    #define x first
    #define y second
    using pii = pair<int, int>;
    using ll = long long;
    
    inline void read(int &x){
        int s=0; x=1;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=1e5+5;
    
    struct Node{
    	int l, r;
    	int go;
    }tr[N*25];
    
    int root[N], idx;
    
    int qwq, n, q, m;
    int w[N];
    
    void upd(int &p, int q, int l, int r, int x, int k){
    	p=++idx;
    	tr[p]=tr[q];
    	if(l==r){;
    		tr[p].go=k;
    		return;
    	}
    	int mid=l+r>>1;
    	if(x<=mid) upd(tr[p].l, tr[q].l, l, mid, x, k);
    	else upd(tr[p].r, tr[q].r, mid+1, r, x, k);
    }
    
    void build(){
    	dwn(i,n,1) upd(root[i-1], root[i], 1, m, w[i], i);
    }
    
    int query(int u, int l, int r, int x){
    	if(l==r) return tr[u].go;
    	int mid=l+r>>1;
    	if(x<=mid) return query(tr[u].l, l, mid, x);
    	return query(tr[u].r, mid+1, r, x);
    }
    
    int main(){
    	cin>>qwq>>n>>q>>m;
    	rep(i,1,n) read(w[i]);
    	
    	build();
    	
    	while(q--){
    		int k; read(k);
    		bool ok=true;
    		int u=0;
    		while(k--){
    			int x; read(x);
    			auto go=query(root[u], 1, m, x);
    			if(!go) ok=false;
    			if(ok) u=go;
    		}
    		puts(ok? "Yes": "No");
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    C#修改SVG图片显示大小
    MyFessttoWord P9 ----UserControl and Side menu Content
    MyFessttoWord P8 ----PageViewModel
    MyFessttoWord Day2--依赖性属性
    西门子Profinet网络连接------实验
    MyFesettoWord_Day1
    ABP 使用Textarea 批量添加数据
    LINQ基础篇(中)
    centos7 cannot find a valid baseurl for repo
    Github上传项目详细教程
  • 原文地址:https://www.cnblogs.com/Tenshi/p/16074377.html
Copyright © 2020-2023  润新知