HDU 6096 (String) AC自动机

来源:互联网 发布:怎么找淘宝客服工作 编辑:程序博客网 时间:2024/05/22 02:23

题目大意

现在有N个单词,Q次询问,每次询问包含两个字符串a和b,求出以a为前缀且以b为后缀的单词共有多少个,其中a和b不互相覆盖。

解题思路

题目中所说的a和b不互相覆盖的含义是当 a =“ac”b = “cm”时,“acm”串不满足条件,因为其以a为前缀且以b为后缀时,a和b相互覆盖了。

题解中所说的做法没太看懂,现在给出一种巧妙构造并利用AC自动机解题的方法。

由于询问的时候,每次给出两个字符串并不便于操作,不妨将后缀与前缀拼接在一起,并使用特殊的符号进行标记。将所有询问离线,建立AC自动机。并将给出的N个单词拼在一起,具体操作为:

1.将每个单词复制一份拼接在原单词后面,中间使用一种分隔符连接,此分隔符与询问中的区分前后缀的分隔符相同,构成新的单词。

2.将新的单词依次连接起来,中间使用另一种分隔符连接,将所有单词连接成一篇文章。

3.原问题转化为,求出一篇文章中的存在的每种模式串的数量,是经典的AC自动机求解问题。

为了方便理解,此处以样例作为栗子

Sample Input

**1
4 4
aba
cde
acdefa
cdef
a a
cd ef
ac a
ce f**

将给出的单词拼接成文章为:

aba|aba#cde|cde#acdefa|acdefa#cdef|cdef#

将前后缀处理为新的字符串:

S1=“a|a”,S2=”ef|cd”,S3=“a|ac”,S4 = “f|ce”

将S1-S4依次insert,建成AC自动机,离线询问

扫描文章,对于每次询问依次回答即可

小Tips:由于题目中给出了前后缀不能覆盖的条件,所以扫描文章的时候需要加入特判。

附上代码

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#define  maxnode 2005000#define sigma 30using namespace std;typedef long long ll;int cnt=1;char que[2550000],in[2000000],head[2000000],tail[2000000],key[2000000];int ask[1001000],res[1010000];struct ac_automation{    int ch[maxnode][sigma];    int val[maxnode];    int last[maxnode];    int f[maxnode];    int num[maxnode];    int dep[maxnode];    int sz,ans;    void clear()    {        sz=1;ans=0;        memset(ch[0],0,sizeof(ch[0]));        memset(last,0,sizeof(last));        memset(val,0,sizeof(val));        memset(num,0,sizeof(num));    }    int idx(char sign)    {        if(sign>='a'&&sign<='z') return sign-'a';        else if(sign=='|') return 26;    }    void insert(char s[],int k)    {        int u=0;        int mx=strlen(s);        for(int i=0;s[i];i++)        {            int c=idx(s[i]);            if(!ch[u][c])            {                memset(ch[sz],0,sizeof(ch[sz]));                ch[u][c]=sz++;            }u=ch[u][c];        }        dep[u]=mx;        if(!val[u])            val[u]=cnt++;        ask[k]=val[u];    }    void build()    {        f[0]=0;        queue<int>q;        for(int i=0;i<sigma;i++)        {            if(ch[0][i])            {                f[ch[0][i]]=0;                q.push(ch[0][i]);                last[ch[0][i]]=0;            }        }        while(!q.empty())        {            int now=q.front();            q.pop();            for(int i=0;i<sigma;i++)            {                int son=ch[now][i];                if(!son)                {                    ch[now][i]=ch[f[now]][i];                    continue;                }                q.push(son);                f[son]=ch[f[now]][i];                last[son]=val[f[son]]?f[son]:last[f[son]];            }        }    }    void find(char *s)    {        int u=0,left=0,right=0,mid=0;        for(int i=0;s[i];i++)        {            if(s[i]=='#') left=i;            else if(s[i]=='|') mid=i;            right=i;            int c=idx(s[i]);            u=ch[u][c];            if(val[u]&&dep[u]<=mid-left)//特殊判断操作,当前后缀之和小于等于原单词长度才进行计数                print(u);            else                print(last[u]);        }    }    void print(int u)    {        if(u)        {            res[val[u]]++;            print(last[u]);        }    }}ac;int main(){//    freopen("1001.in","r",stdin);//    freopen("out.txt","w",stdout);    int t,n,q;    scanf("%d",&t);    while(t--)    {        memset(res,0,sizeof(res));        ac.clear();        scanf("%d%d",&n,&q);        int cont=1;        que[0]='#';        for(int i=0;i<n;i++)        {            scanf("%s",in);            int len=strlen(in);            for(int j=0;j<len;j++)                que[cont+j]=in[j];            que[cont+len]='|';            cont=cont+len+1;            for(int j=0;j<len;j++)                que[cont+j]=in[j];            que[cont+len]='#';            cont=cont+len+1;        }        que[cont]=0;        for(int i=1;i<=q;i++)        {            scanf("%s",head);            scanf("%s",tail);            int len=strlen(tail);            tail[len]='|';len++;            int len2=strlen(head);            for(int j=0;j<len2;j++)                tail[len+j]=head[j];            tail[len+len2]=0;            ac.insert(tail,i);        }        ac.build();        ac.find(que);        for(int i=1;i<=q;i++)            printf("%d\n",res[ask[i]]);    }    return 0;}
原创粉丝点击