「Luogu P5826 【模板】子序列自动机」

不会用STL的路过

题目大意

给出一个序列 \(a\) 和一些序列,需要判断这些序列是否是 \(a\) 的字序列.

分析

这篇文章不会用到vector,保证可以让pascal等语言的用户也看懂并且可以轻松实现.

序列自动机

如何快速判断一个序列是否是另一个序列的子序列,这时就可以用到序列自动机了,对于暴力的做法可以枚举需要查询的序列的第 \(i\) 个位置,在用一个类似指针的东西枚举在原序列中的下一个相等的位置,可以发现这样的时间复杂度为\(\mathcal{O}(|a|)\)(\(|a|\)表示原序列的长度,末日查询序列长度小于原序列长度),而序列自动机就是讲每个位置的下一个出现元素的位置全部保存,这样就可以做到 \(\mathcal{O}(1)\) 查询下一个出现的位置了,但是这个东西的的预处理的时间复杂度为 \(\mathcal{O}(\) 值域大小 \(\times |a|)\),显然会MLE,那么可以发现对于相邻的两个数的这个查询下一个位置的数组最多只有一个位置不一样,所以,就是可以持久化线段树就可以解决了.

最后的时间复杂度为 \(\mathcal{O}(\sum_{i=1}^qL_i\log_2m+n\log_2m)\).

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int maxN=1e6+7;
int N,M;
int top=0;
int a[maxN],b[maxN];
struct Tree//主席树模板,不多讲了
{
    int lson,rson,sum;
}tree[maxN*80];
int point_cnt=0;
#define LSON tree[now].lson
#define RSON tree[now].rson
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
#define NEW_LSON tree[new_tree].lson
#define NEW_RSON tree[new_tree].rson
void Updata(int num,int change,int &new_tree,int now,int left=1,int right=top)
{
    if(num<left||right<num)
    {
        new_tree=now;
        return;
    }
    new_tree=++point_cnt;
    tree[new_tree].lson=0;
    tree[new_tree].rson=0;
    tree[new_tree].sum=0;
    if(left==right)
    {
        tree[new_tree].sum=change;
        return;
    }
    if(MIDDLE>=num)
    Updata(num,change,NEW_LSON,LEFT);
    else
    NEW_LSON=LSON;
    if(MIDDLE+1<=num)
    Updata(num,change,NEW_RSON,RIGHT);
    else
    NEW_RSON=RSON;
}
int Query(int num,int now,int left=1,int right=top)
{
    if(num<left||right<num)
    {
        return 0;
    }
    if(left==right)
    {
        return tree[now].sum;
    }
    return Query(num,LEFT)+Query(num,RIGHT);
}
int root[maxN];
void work()//对于每一个序列
{
    point_cnt=0;
    scanf("%d",&M);
    int now=1;
    REP(i,1,M)
    {
        scanf("%d",&b[i]);
    }
    REP(i,1,M)
    {
        now=Query(b[i],root[now]);//查询这个数下一个出现的位置
        if(now==0)//如果不存在就是No
        {
            printf("No\n");
            return;
        }
        now++;//这里记录的是包含自己的所以需要++
    }
    printf("Yes\n");//存在
}
int main()
{
    int opt;
    int T;
    scanf("%d%d%d%d",&opt,&T,&N,&top);
    REP(i,1,N)
    {
        scanf("%d",&a[i]);
    }
    DOW(i,N,1)//倒叙预处理
    {
        Updata(a[i],i,root[i],root[i+1]);
    }
    REP(i,1,T)
    {
        work();
    }
    return 0;
}
上一篇:POJ3667线段树区间合并


下一篇:Huffman树和Huffman编码