暑假 D6 T1 substring(AC自动机)

题目描述

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

输入格式

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

第二行为长为n的文本串

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

输出格式

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

数据范围

对于100%的数据n,Q≤100000,且保证S为长度不超过100000的回文串,且所有的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\n");
        else printf("NO\n");
    }
    return 0;
}
/*
6 3
abdbec
e
bdb
aa
*/

不过好像可以直接用补全的AC自动机,查询就在树上跑文本串,对于每一个节点跳fail,对于要求的S的结尾存下询问标号,遍历到他就记录。

不过好像差不多。

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

时间: 2024-10-05 11:09:26

暑假 D6 T1 substring(AC自动机)的相关文章

UVA 11468 - Substring(AC自动机)

UVA 11468 - Substring 题目链接 题意:给定一些模式串,然后给出一些字母出现的概率,每次随机出现一个字母,要求出这些字母出现L个组成的字符串不包含(即不是它的连续字串)已有模式串的概率 思路:AC自动机,先构造出AC自动机,构造的时候利用next数组的特性,记录下每个位置是否有经过一个单词结点,如果有这个结点就是不能走的结点,那么问题就变成了只能在能走的结点上走L步的概率,注意这里空边也要处理成可以走(走到它的next,因为不匹配的话就一直找到next能匹配的位置),然后进行

uva 11468 - Substring(AC自动机+概率)

题目链接:uva 11468 - Substring 题目大意:给出一些字符和各自字符对应的选择概率,随机选择L次后得到一个长度为L的字符串,要求该字符串不包含任意一个子串的概率. 解题思路:构造AC自动机之后,每随机生成一个字母,等于是在AC自动机上走一步,所有子串的结束位置的节点标记为禁止通行,然后问题转换成记忆搜索处理. #include <cstdio> #include <cstring> #include <queue> #include <algor

UVa 11468 Substring (AC自动机+概率DP)

题意:给出一个字母表以及每个字母出现的概率.再给出一些模板串S.从字母表中每次随机拿出一个字母,一共拿L次组成一个产度为L的串, 问这个串不包含S中任何一个串的概率为多少? 析:先构造一个AC自动机,然后随机生成L个字母,就是在AC自动机的某个结点走多少步,dp[i][j] 表示在 i 结点,并且剩下 j 步, 然后记忆化搜索就OK了. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <

【暑假】[实用数据结构] AC自动机

Aho-Corasick自动机 AC自动机用于解决文本一个而模板有多个的问题. 作者所给模板如下: 1 struct AhoCorasickAutomata { 2 int ch[MAXNODE][SIGMA_SIZE]; 3 int f[MAXNODE]; // fail函数 4 int val[MAXNODE]; // 每个字符串的结尾结点都有一个非0的val 5 int last[MAXNODE]; // 输出链表的下一个结点 6 int cnt[MAXS]; 7 int sz; 8 9

AC自动机

AC自动机 直接学AC自动机比较难理解,强烈建议先学完KMP和字典树并进行一定的练习后,对于失配指针和字典树构造有一定理解后再来学AC自动机的内容.有关AC自动机的详细介绍可见刘汝佳的<算法竞赛入门经典训练指南>P214. 给你一个字典(包含n个不重复的单词),然后给你一串连续的字符串文本(长为len),问你该文本里面的哪些位置正好出现了字典中的某一个或某几个单词?输出这些位置以及出现的单词. 这个问题可以用n个单词的n次KMP算法来做(效率为O(n*len*单词平均长度)),也可以用1个字典

暑假集训day9补充(AC自动机)

推荐网站http://blog.csdn.net/niushuai666/article/details/7002823 AC自动机嘛,此AC(aho-corasick)非彼AC(Accepted). 我也不是很会解释 有一题是必须打的hdu2222. #include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int mn=

LightOJ 1427 Substring Frequency (II) (AC自动机)

题意:给定一个文本串和 n 个子串,问你子串在文本串出现的次数. 析:很明显的AC自动机,只要把先把子串进行失配处理,然后再去用文本串去匹配,在插入子串时就要标记每个串,注意串可能是相同的,这个我错了两次,最后匹配一次就OK了. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib

UVA 11468 Substring (AC自动机)

用把失配边也加到正常边以后AC自动机,状态是长度递减的DAG,每个选一个不会匹配字符的转移. dp[u][L]表示当前在tire树上u结点长度还剩L时候不匹配的概率,根据全概率公式跑记忆化搜索. #include<bits/stdc++.h> using namespace std; typedef double ld; const int maxnds = 21*21, sigma_size = 62; int nds; int ch[maxnds][sigma_size]; double

UVa 11468 (AC自动机 概率DP) Substring

将K个模板串构成一个AC自动机,那些能匹配到的单词节点都称之为禁止节点. 然后问题就变成了在Tire树上走L步且不经过禁止节点的概率. 根据全概率公式用记忆化搜索求解. 1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 using namespace std; 5 6 const int maxnode = 500; 7 const int sigma_size = 64; 8 int idx[2