HDU 6096 String(AC自动机)

【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=6096

【题目大意】

  给出一些字符串,给出前缀后缀模式询问,问有多少字符串符合该模式

【题解】

  我们将字符串变为双倍,在中间增加拼接符,
  对于每个前后缀模式,我们将其处理为[后缀+拼接符+前缀]的形式,
  那么原题等价于统计前后缀模式在多少个字符串中出现,
  我们用所有的前后缀模式建立AC自动机,用每个字符串在AC自动机上跑匹配,
  最后统计fail链前继累加和即可。

【代码】

#include <cstdio>
#include <cstring>
using namespace std;
const int N=1200010;
namespace AC_DFA{
    const int Csize=27;
    int tot,son[N][Csize],sum[N],fail[N],q[N],ans[N],dph[N],match[N];
    void Initialize(){
        memset(sum,0,sizeof(int)*(tot+1));
        memset(dph,0,sizeof(int)*(tot+1));
        memset(ans,0,sizeof(int)*(tot+1));
        memset(match,0,sizeof(int)*(tot+1));
        memset(fail,0,sizeof(int)*(tot+1));
        for(int i=0;i<=tot;i++)for(int j=0;j<Csize;j++)son[i][j]=0;
        tot=0; fail[0]=-1;
    }
    inline int Tr(char ch){return ch-‘a‘;}
    int Insert(char *s){
        int x=0;
        for(int l=strlen(s),i=0,w;i<l;i++){
            if(!son[x][w=Tr(s[i])]){
                son[x][w]=++tot;
                dph[tot]=i+1;
            }x=son[x][w];
        }sum[x]++;
        return x;
    }
    void MakeFail(){
        int h=1,t=0,i,j,x;
        for(i=0;i<Csize;i++)if(son[0][i])q[++t]=son[0][i];
        while(h<=t)for(x=q[h++],i=0;i<Csize;i++)
        if(son[x][i]){
            fail[son[x][i]]=son[fail[x]][i],q[++t]=son[x][i];
            match[son[x][i]]=sum[son[x][i]]?son[x][i]:match[fail[son[x][i]]];
        }else son[x][i]=son[fail[x]][i];
    }
    void Search(char *s,int len){
        for(int l=strlen(s),i=0,x=0,w;i<l;i++){
            x=son[x][Tr(s[i])];
            while(dph[x]>len)x=fail[x];
            ans[match[x]]++;
        }
    }
    int d[N],st[N];
    void Solve(){
        int k=0;
        memset(d,0,sizeof(int)*(tot+1));
        for(int i=1;i<=tot;i++)d[fail[i]]++;
        for(int i=1;i<=tot;i++)if(!d[i])st[k++]=i;
        for(int i=0;i<k;i++){
            int j=fail[st[i]];
            ans[j]+=ans[st[i]];
            if(!--d[j])st[k++]=j;
        }
    }
}
int len[100010],pos[100010];
char ss[N],t1[N],t2[N];
char *s[100010];
using namespace AC_DFA;
int main(){
    int T,n,q;
    scanf("%d",&T);
    while(T--){
        Initialize();
        scanf("%d%d",&n,&q);
        for(int i=0,j=0;i<n;i++){
            s[i]=ss+j;
            scanf("%s",s[i]);
            len[i]=strlen(s[i])+1;
            j+=len[i];
            strcpy(ss+j,s[i]);
            ss[j-1]=‘z‘+1;
            j+=len[i];
        }
        for(int i=0;i<q;i++){
            t1[0]=‘z‘+1;
            scanf("%s%s",t1+1,t2);
            strcat(t2,t1);
            pos[i]=Insert(t2);
        }MakeFail();
        for(int i=0;i<n;i++)Search(s[i],len[i]);
        Solve();
        for(int i=0;i<q;i++)printf("%d\n",ans[pos[i]]);
    }return 0;
}

  

时间: 2024-10-04 10:25:24

HDU 6096 String(AC自动机)的相关文章

2017多校第6场 HDU 6096 String AC自动机

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6096 题意:给了一些模式串,然后再给出一些文本串的不想交的前后缀,问文本串在模式串的出现次数. 解法: 因为要求前缀后缀都包含的个数,所以可以把字符串a转换成a#a这样一个字符串,比如abca就转换成abca#abca 然后对于一组前缀a后缀b转换成b{a,比如ab ca,就是ca{ab, 然后对前缀后缀的串建立AC自动机,让主串去匹配,如上述例子,ca{ab满足为abca{abca的一个子串,也就

HDU 6096 String (AC自动机)

题意:给出n个字符串和q个询问,每次询问给出两个串 p 和 s .要求统计所有字符串中前缀为 p 且后缀为 s (不可重叠)的字符串的数量. 析:真是觉得没有思路啊,看了官方题解,真是好复杂. 假设原始的字符串 数组为A,首先将A中的每个字符串都进行翻转,得到字符串数组B,然后,将A和B按字典序排序. 对于一个查询来说有一个前缀p和后缀s, 所有包含前缀p的字符串在A中是连续的,可通过二分求出该区间 设为[Lp,Rp],同样,所有包含后缀s的字符串在B中也是连续的,设为[Ls,Rs] 接下来只需

HDU 6096 String(AC自动机+树状数组)

题意 给定 \(n\) 个单词,\(q\) 个询问,每个询问包含两个串 \(s_1,s_2\),询问有多少个单词以 \(s_1\) 为前缀, \(s_2\) 为后缀,前后缀不能重叠. \(1 \leq n,q \leq 10^5\) 思路 字符串题有一个小技巧,拼接字符串,中间加上连接符.如这道题,可以将查询变成 \(s_2+\text{\{}+s_1\) 的形式,相应的,把单词 \(T\) 变为 \(T+\text{\{}+T\) 的形式.那么就是普通的匹配问题了. 对于询问建立\(\text

HDU - 6086 Rikka with String AC自动机 + dp

HDU - 6086 前缀和后缀分别建AC自动机, 考虑从两端往中间dp dp[ o ][ i ][ j ][ mask ] 表示放了前面和后面o个, 第一个自动机在 i 位置, 第二个自动机在 j 位置, 拥有的目标串的状态是mask的方案数. 对于跨过两端的东西, 我们最后处理就好了. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #def

HDU 2222(AC自动机模板题)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意:多个模式串.问匹配串中含有多少个模式串.注意模式串有重复,所以要累计重复结果. 解题思路: AC自动机模板题. 一开始使用LRJ的坑爹静态模板,不支持重复的模式串. 在做AC自动机+DP的时候,扒了zcwwzdjn大神的动态优化(失配指向root)写法,以及借鉴了网上的AC自动机模板, 搞出了这么一个支持重复串的模板. #include "cstdio" #include

HDU 2296 Ring [AC自动机 DP 打印方案]

Ring Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3536 Accepted Submission(s): 1153 Problem Description For the hope of a forever love, Steven is planning to send a ring to Jane with a romantic

zoj 3228 Searching the String(AC自动机)

题目连接:zoj 3228 Searching the String 题目大意:给定一个字符串,然后现在有N次询问,每次有一个type和一个子串,问说子串在字符串中出现几次,type 为0时为可重叠,为1时为不可重叠. 解题思路:不过没有type=1的限制,那么就是普通的AC自动机匹配问题,对于不可重叠问题,可以对于每个节点记录 一下上一次匹配到的pos,用当前匹配的i减掉pos看有没有超过长度,有超过即为合法匹配,否则忽略. 题目中有很多相同的子串,一开始我用jump数组用类似链表的形式记录每

HDU 2222 (AC自动机模板题)

题意: 给一个文本串和多个模式串,求文本串中一共出现多少次模式串 分析: ac自动机模板,关键是失配函数 #include <map> #include <set> #include <list> #include <cmath> #include <queue> #include <stack> #include <cstdio> #include <vector> #include <string&g

HDU 2296 Ring AC自动机 + DP

题意:给你n个模式串,每个模式串有一个得分,让你构造出一个长度为N之内且分数最高的文本串;输出字典序列最小的. 解题思路:  AC自动机 + DP , 不过要输出字典序列最小,多开一个 一个三维字符串来辅助二维DP(新思路) , DP[i][j] ,表示到i位置状态为j的最大得分. 解题代码: 1 // File Name: temp.cpp 2 // Author: darkdream 3 // Created Time: 2014年09月11日 星期四 15时18分4秒 4 5 #inclu