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

Posted Lsxxxxxxxxxxxxx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的一个子串,也就是abca满足这个前缀后缀,所以问题,就转换成了典型的ac自动机匹配问题。

加个{的原因是为了只让后缀{前缀这种串能在AC自动机匹配到。

然后求答案的时候,需要对连接到自己的fail的位置累加一下,含义想一下就明白了。

 

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
const int maxs = 30;
string s[maxn];
int d[maxn],pos[maxn],ans[maxn],st[maxn],len[maxn];
struct Acautomata{
    int nxt[maxn][maxs],cnt[maxn],fail[maxn],match[maxn],dep[maxn];
    int L, root;
    int newnode(){
        for(int i=0; i<maxs; i++) nxt[L][i]=-1;
        cnt[L++]=0;
        fail[L-1]=0;
        return L-1;
    }
    void init(){
        L = 0;
        root = newnode();
    }
    int Insert(string a){
        int now=root;
        for(int i=0; a[i]; i++){
            if(nxt[now][a[i]-‘a‘]==-1){
                nxt[now][a[i]-‘a‘]=newnode();
                dep[L-1]=i+1;
            }
            now=nxt[now][a[i]-‘a‘];
        }
        cnt[now]=1;
        return now;
    }
    void build()
    {
        queue<int>q;
        int now=root;
        fail[0] = 0;
        for(int i=0; i<maxs; i++){
            if(nxt[now][i]==-1){
                nxt[now][i]=root;
                continue;
            }
            else{
                fail[nxt[now][i]]=root;
                q.push(nxt[now][i]);
            }
        }
        while(!q.empty())
        {
            now = q.front();
            q.pop();
            for(int i=0; i<maxs; i++){
                if(nxt[now][i]!=-1){
                    q.push(nxt[now][i]);
                    fail[nxt[now][i]] = nxt[fail[now]][i];
                    match[nxt[now][i]] = cnt[nxt[now][i]]?nxt[now][i]:match[nxt[fail[now]][i]];
                }else{
                    nxt[now][i] = nxt[fail[now]][i];
                }
            }
        }
    }
    void solve1(string a, int h){
        int now=root;
        int sz = a.size();
        for(int i=0; i<sz; i++){
            now=nxt[now][a[i]-‘a‘];
            while(dep[now]>h){
                now=fail[now];
            }
            ans[match[now]]++;
        }
    }
    void solve2(){//累加答案,对连到自己的fail进行累加求和
        memset(d, 0, sizeof(d));
        int j, k = 0;
        for(int i=0; i<L; i++) d[fail[i]]++;
        for(int i=0; i<L; i++) if(!d[i]) st[k++]=i;
        for(int i=0; i<k; i++){
            j=fail[st[i]];
            ans[j]+=ans[st[i]];
            if(!(--d[j])){
                st[k++]=j;
            }
        }
    }
}ZXY;
int n, q;
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        ZXY.init();
        scanf("%d%d", &n,&q);
        for(int i=0; i<n; i++){
            cin>>s[i];
            len[i] = s[i].size()+1;
            string temp=s[i];
            s[i]+=char(‘z‘+1);
            s[i]+=temp;
        }
        for(int i=0; i<q; i++){
            string s1,s2;
            cin>>s1>>s2;
            s2 = s2 + char(‘z‘+1);
            s2 = s2 + s1;
            pos[i] = ZXY.Insert(s2);
        }
        ZXY.build();
        for(int i=0; i<n; i++){
            ZXY.solve1(s[i],len[i]);
        }
        ZXY.solve2();
        for(int i=0; i<q; i++) printf("%d\n", ans[pos[i]]);
        for(int i=0; i<=ZXY.L; i++){
            ans[i]=0;
            ZXY.match[i]=0;
        }
    }
    return 0;
}

 

以上是关于2017多校第6场 HDU 6096 String AC自动机的主要内容,如果未能解决你的问题,请参考以下文章

2017多校第6场 HDU 6097 Mindis 计算几何,圆的反演

HDU 5358 多校第6场 First One

2018多校第6场 1013 hdu6373 Pinball

2017多校第10场 HDU 6172 Array Challenge 猜公式,矩阵幂

2017多校第10场 HDU 6181 Two Paths 次短路

2017多校第10场 HDU 6180 Schedule 贪心,multiset