暑假 D6 T1 substring(AC自动机)

题目描述

给出一个长度为n的文本串,有Q次询问,每次给出一个字符串S,询问S是否在文本串中作为子串出现过

输入格式

第一行两个整数n和Q,分别表示文本串长度和询问次数

第二行为长为n的文本串

接下来Q行,每行为一个字符串S

输出格式

输出Q行对应Q次询问的答案,若出现过则输出YES,否则输出NO

数据范围

对于100%的数据n,Q≤100000,且保证s为长度不超过10000的回文串且所有s的总长不超过1000000,保证所有字符都是小写字母

题解

看到题目就想起了阿狸的打字机,而且还只是遍历一条路径

栈的范围没改到崩了可还行

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;
const int maxm=1000005;
int n,m,num,cnt;
int go[maxm][26],mp[maxn];
int gogo[maxm][26],fail[maxm];//trie图 
int in[maxm],size[maxm];
char s[maxn],t[maxn];
int head[maxm];
struct edge{
    int y,next;
}e[maxm<<1];
int tr[maxm<<1];

void add1(int len){
    int now=0;
    for(int i=0;i<len;i++){
        int c=s[i]-'a';
        if(!go[now][c]) gogo[now][c]=go[now][c]=++num;
        now=go[now][c];
    }
    mp[++cnt]=now;
}

void addedge(int x,int y){
    e[++cnt]=(edge){y,head[x]};
    head[x]=cnt;
}

int q[maxm];
void get_fail(){
    cnt=0;
    int h=0,tail=0;
    for(int i=0;i<26;i++) if(gogo[0][i]){q[tail++]=gogo[0][i];addedge(0,gogo[0][i]);}
    while(h<tail){
        int x=q[h++];
        for(int i=0;i<26;i++){
            if(!gogo[x][i]) gogo[x][i]=gogo[fail[x]][i];
            else{
                fail[gogo[x][i]]=gogo[fail[x]][i];
                addedge(gogo[fail[x]][i],gogo[x][i]);
                q[tail++]=gogo[x][i];
            }
        }
    }
}

void dfs(int u){
    in[u]=++cnt;size[u]=1;
    for(int i=head[u];i;i=e[i].next){
        dfs(e[i].y);
        size[u]+=size[e[i].y];
    }
}

void add(int x,int val){
    for(;x<=cnt;x+=x&-x) tr[x]+=val;
}

int sum(int x){
    int ret=0;
    for(;x;x-=x&-x) ret+=tr[x];
    return ret;
}

int main(){
    freopen("substring.in","r",stdin);
    freopen("substring.out","w",stdout);
    scanf("%d%d",&n,&m);
    scanf("%s",t);
    int now=0;
    for(int i=0;i<n;i++){
        int c=t[i]-'a';
        if(!go[now][c]) gogo[now][c]=go[now][c]=++num;
        now=go[now][c];
        //printf("%d ",now);
    }
    for(int i=1;i<=m;i++){
        scanf("%s",s);
        add1(strlen(s));
    }
    get_fail(); 
    cnt=0;
    dfs(0);
    for(int i=1;i<=n;i++) add(in[i],1);
    for(int i=1;i<=m;i++){
        int x=mp[i];//printf("%d ",x);
        if(sum(in[x]+size[x]-1)-sum(in[x]-1)) printf("YES
");
        else printf("NO
");
    }
    return 0;
}
/*
6 3
abdbec
e
bdb
aa
*/
View Code

 直接用补全的AC自动机也可以,最后查询文本串,在每个点一直跳fail,(就是阿狸的暴力

原文地址:https://www.cnblogs.com/sto324/p/11191505.html