AC自动机

模板题hdu 2222http://acm.hdu.edu.cn/showproblem.php?pid=2222

#include <iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<algorithm>
using namespace std;
struct node
{
    int cnt;//是否为该单词的最后一个节点
    node *fail;//失败指针
    node *nextt[130];
    node()
    {
        cnt=0;
        fail=0;
        memset(nextt,NULL,sizeof(nextt));
    }
};//队列,方便用BFS构造失败指针
char s[1000005];//主字符串
char keyword[55];//需要查找的单词
int head,tail;
node *root;//头节点
/*void init(node *root)
{
    root->cnt=0;
    root->fail=NULL;
    for(int i=0;i<130;i++)
        root->nextt[i]=NULL;
}*/
void build_trie(char *keyword)
{
   // node *p,*q;
   node *p=root;
    int i,v;
    int len=strlen(keyword);
    for(i=0;i<len;i++)
    {
       // v=keyword[i]-'a';
       v=keyword[i];//注意这里,想要这样用的话,把nextt数组开大一点即可,例如130
        if(p->nextt[v]==NULL)
        {
            //q=(struct ndoe*)malloc(sizeof(node));
 
           // q=(struct node*)malloc(sizeof(node));
           // q=new node;
            //init(q);
            p->nextt[v]=new node();//节点链接
        }
        p=p->nextt[v];
    }
    p->cnt++;//单词最后一个节点cnt++,代表一个单词
}
void build_ac_fail(node *root)
{
    queue <node *> que;
    root->fail=NULL;
    que.push(root);
    while(!que.empty())
    {
        node *temp=que.front();
        que.pop();
        node *p=NULL;
        for(int i=0; i<130; i++)
        {
            if(temp->nextt[i]!=NULL)
            {
                if(temp==root) temp->nextt[i]->fail=root;
                else
                {
                    p=temp->fail;
                    while(p!=NULL)
                    {
                        if(p->nextt[i]!=NULL)
                        {
                            temp->nextt[i]->fail=p->nextt[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL)
                        temp->nextt[i]->fail=root;
                }
                que.push(temp->nextt[i]);
            }
        }
    }
 
 
}
/*void build_ac_fail(node *root)
{
    head=0,tail=0;//队列头、尾指针
    queuet[head++]=root;//先将root入队
    while(head!=tail)
    {
        node *p=NULL;
        node *temp=queuet[tail++];//弹出队头结点
        for(int i=0;i<130;i++)
        {
            if(temp->nextt[i]!=NULL)//找到实际存在的字符节点
            {
                //temp->nextt[i] 为该节点,temp为其父节点
                if(temp==root)//若是第一层中的字符节点,则把该节点指针指向root
                    temp->nextt[i]->fail=root;
                else
                    {
                        //依次回溯该节点的父节点的失败指针直到某节点的nextt[i]与该节点相同
                        //则把该节点的失败指针指向该nextt[i]节点
                        //若回溯到root都没有找到,则该节点的失败指针指向root
                        p=temp->fail;//把该节点的父节点的失败指针给p
                        while(p!=NULL)
                        {
                            if(p->nextt[i]!=NULL)
                            {
                                temp->nextt[i]->fail=p->nextt[i];
                                break;
                            }
                            p=p->fail;
                        }
                        //让该节点的失败指针也指向root
                        if(p==NULL)
                            temp->nextt[i]->fail=root;
                    }
                    queuet[head++]=temp->nextt[i];//每处理一个节点,都让该节点依次入队
            }
        }
    }
*/
int query(node *root)//匹配
{
    //i为主串指针,p为模式串指针
    int i,v,countt=0;
    node *p=root;
    int len=strlen(s);
    for(i=0;i<len;i++)
    {
       // v=s[i]-'a';
       v=s[i];
        //由失败指针回溯查找,判断s[i]是否存在于trie树中
        while(p->nextt[v]==NULL&&p!=root)
            p=p->fail;
        p=p->nextt[v];//找到后p指针指向该节点
        if(p==NULL)//若指针返回为空,则没有找到与之匹配的字符
            p=root;
        node *temp=p;//匹配该节点后,沿其失败指针回溯,判断其它节点是否匹配
        while(temp!=root)//匹配结束控制
        {
            if(temp->cnt>=0)//判断该节点是否被访问
            {
                countt+=temp->cnt;//由于cnt初始化为0,所以cnt>0时才统计了单词的个数
                temp->cnt=-1;//标记已访问过
            }
            else//节点已经访问,退出循环
                break;
            temp=temp->fail;//回溯,失败指针,继续寻找下一个节点
        }
    }
    return countt;
}
int main()
{
   int T,n;
   cin>>T;
   while(T--)
   {
       /*root=(struct node*)malloc(sizeof(node));
       init(root);*/
       root=new node();
       cin>>n;
       for(int i=0;i<n;i++)
       {
           scanf("\n%s",keyword);
           build_trie(keyword);
       }
       build_ac_fail(root);
       scanf("\n%s",s);
       printf("%d\n",query(root));
   }
    return 0;
}

hdu2896http://acm.hdu.edu.cn/showproblem.php?pid=2896

/*依次按如下格式输出按网站编号从小到大输出,带病毒的网站编号和包含病毒编号,每行一个含毒网站信息。
web 网站编号: 病毒编号 病毒编号 …
冒号后有一个空格,病毒编号按从小到大排列,两个病毒编号之间用一个空格隔开,如果一个网站包含病毒,病毒数不会超过3个。
最后一行输出统计信息,如下格式
total: 带病毒网站数
冒号后有一个空格。
Sample Input
3
aaa
bbb
ccc
2
aaabbbccc
bbaacc
Sample Output
web 1: 1 2 3
total: 1*/
#include <iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=10010;//在hdu2896这道题中,我的初始化出现了问题
struct node
{
    int cnt;//是否为该单词的最后一个节点
    int code;
    node *fail;//失败指针
    node *nextt[130];
    node()
    {
        fail=NULL;
        code=0;
        cnt=0;
        memset(nextt,NULL,sizeof(nextt));
    }
 
};//队列,方便用BFS构造失败指针
 
//需要查找的单词
 
int ans[5];//标记主串中含有病毒的编号
 
/*void init(node *root)//我用这个函数,提交超内存
{
    root->cnt=0;
    root->code=0;
    root->fail=NULL;
    for(int i=0; i<130; i++)//注意这里是大小写字母均存在,用130 这个范围,以后如果要谜面这种情况,直接用构造函数,sizeof(nextt)
        root->nextt[i]=NULL;
}*/
void build_trie(node*root,char *keyword,int x)
{
    node *p=root;
    int i=0,v;
   int len=strlen(keyword);
   /*while(keyword[i])
    {
          v=keyword[i];
        if(p->nextt[v]==NULL)
        {
           p->nextt[v]=new node();
        }
        p=p->nextt[v];
        i++;
    }*/
 
   for(i=0,p=root; i<len; i++)
    {
        v=keyword[i];//这个地方很巧妙。可以不用向上次那样keyword[i]-'a'
        if(p->nextt[v]==NULL)
        {
        //q=new node;
          //  init(q);
           // p->nextt[v]=q;
            p->nextt[v]=new node();//节点链接
        }
        p=p->nextt[v];
    }
    p->code=x;
    // p->cnt=1;
    p->cnt++;//单词最后一个节点cnt++,代表一个单词
}
void build_ac_fail(node *root)
{
    queue <node *> que;
    root->fail=NULL;
    que.push(root);
    while(!que.empty())
    {
        node *temp=que.front();
        que.pop();
        node *p=NULL;
        for(int i=0; i<130; i++)
        {
            if(temp->nextt[i]!=NULL)
            {
                if(temp==root) temp->nextt[i]->fail=root;
                else
                {
                    p=temp->fail;
                    while(p!=NULL)
                    {
                        if(p->nextt[i]!=NULL)
                        {
                            temp->nextt[i]->fail=p->nextt[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL)
                        temp->nextt[i]->fail=root;
                }
                que.push(temp->nextt[i]);
            }
        }
    }
 
 
}
int query(node *root,char *str)
{
    int i=0,cnnt=0,index;
    node *p=root;
    while(str[i])
    {
        index=str[i];
        while(p->nextt[index]==NULL&&p!=root)
            p=p->fail;
        p=p->nextt[index];
        if(p==NULL) p=root;
        node *temp=p;
        while(temp!=root&&temp->code)
        {
            ans[cnnt]=temp->code;
            cnnt+=p->cnt;
            temp=temp->fail;
        }
        i++;
    }
    return cnnt;
}
 
int main()
{
    int T;
    char s[maxn];//主字符串
    char keyword[205];
    while(scanf("%d",&T)!=EOF)
    {
       node * root=new node();
       /* node *root=new node;
        init(root);*/
        for(int i=1; i<=T; i++)
        {
            scanf("%s",keyword);
 
            build_trie(root,keyword,i);
        }
        build_ac_fail(root);
        int sum=0;
        int M;
        scanf("%d",&M);
        for(int i=1; i<=M; i++)
        {
            scanf("%s",s);
            int flag=0;
            int num=query(root,s);
           if(num)
            {
                flag=true;
                printf("web %d:",i);
                sort(ans,ans+num);
                for(int j=0; j<num; j++)
                    printf(" %d",ans[j]);
                printf("\n");
            }
            if(flag)
                sum++;
        }
        printf("total: %d\n",sum);
    }
 
    return 0;

}

hdu3065

/*Input
第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。
在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。
Output
按以下格式每行一个,输出每个病毒出现次数。未出现的病毒不需要输出。
病毒特征码: 出现次数
冒号后有一个空格,按病毒特征码的输入顺序进行输出。 Sample Input
3
AA
BB
CC
ooxxCC%dAAAoen....END
Sample Output
AA: 2
CC: 1



Hint
Hit:
题目描述中没有被提及的所有情况都应该进行考虑。比如两个病毒特征码可能有相互包含或者有重叠的特征码段。
计数策略也可一定程度上从Sample中推测。

       */
#include <iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<string>
using namespace std;
const int maxn=2000005;
//map<string,int> mp;
struct node
{
    int cnt,index;
    //char code[1005][55];
  //string code;
    node *fail;
    node *nextt[27];
    node()
    {
        cnt=0;
        fail=NULL;
        index=0;
        memset(nextt,NULL,sizeof(nextt));
     //  code="";
    }
};
//char code[1005][55];
//char s[maxn];
//char keyword[55];
int ans[1005];
//node *root;
void build_trie(node *root,char keyword[],int inde)
{
    node *p=root;
    int i,v;
    int len=strlen(keyword);
    for( i=0;i<len;i++)
    {
        v=keyword[i]-'A';
        if(p->nextt[v]==NULL)
            p->nextt[v]=new node();
        p=p->nextt[v];
    }
    p->index=inde;
    //p->code=keyword;
   // np[p->code]=p->index;
    p->cnt++;
}
void build_ac_fail(node *root)
{
    queue<node *> que;
    root->fail=NULL;
    que.push(root);
    while(!que.empty())
    {
        node *temp=que.front();
        que.pop();
        node *p=NULL;
        for(int i=0;i<27;i++)
        {
            if(temp->nextt[i]!=NULL)
            {
                if(temp==root)
                    temp->nextt[i]->fail=root;
                else
                {

                    p=temp->fail;
                    while(p!=NULL)
                    {
                        if(p->nextt[i]!=NULL)
                        {
                            temp->nextt[i]->fail=p->nextt[i];
                            break;
                        }
                         p=p->fail;
                    }
                    if(p==NULL)
                        temp->nextt[i]->fail=root;
                }
                que.push(temp->nextt[i]);

            }
        }
    }
}
void query(node *root,char * s)
{
    int i,v;
    node *p=root;
    int len=strlen(s);
    for(i=0;i<len;i++)
    {
        if(s[i]<'A'||s[i]>'Z')//因为特征码全为大写字母,所以可以令除了大写字母的字符都为‘Z'+1
            s[i]='Z'+1;
        v=s[i]-'A';
        while(p->nextt[v]==NULL&&p!=root)
            p=p->fail;
        p=p->nextt[v];
        if(p==NULL)
            p=root;
        node* temp=p;
        while(temp!=root)
        {
            if(temp->index) 
            {
                //temp->cnt++;
                ans[temp->index]=temp->cnt;
                temp->cnt++;
            }
        /*  if(temp->cnt>=0)
          {
               mp[temp->code]++;
          }*/

            temp=temp->fail;
            /*f(temp->cnt>=0)
            {
                              mp[]+=1;
                temp->cnt=-1;
            }
            else
                break;
            temp=temp->fail;*/
        }
    }
}
int main()
{
    int T;
    char s[maxn];
    char keyword[1005][55];
    while(scanf("%d",&T)!=EOF)
    {
        node *root=new node();
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=T;i++)
        {
            scanf("%s",keyword[i]);
            build_trie(root,keyword[i],i);
        }
        build_ac_fail(root);
        scanf("%s",s);
        query(root,s);
        for(int i=1;i<=T;i++)
        {
            if(ans[i]!=0)
                cout<<keyword[i]<<": "<<ans[i]<<endl;

        }

    }
    return 0;
}

十个利用矩阵乘法解决的经典题目http://www.matrix67.com/blog/archives/276

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值