7.6.2 Keywords Search(AC自动机)

任务描述

现在,搜索引擎已经走进了每个人的生活,比如,大家使用Google、百度,等等。 Wiskey希望将搜索引擎引入到他的图像检索系统中。 每个图像都有一个很长的文字描述,当用户键入一些关键字来查找图像时,系统会将关键字与图像的文字描述进行匹配,并显示出匹配关键字最多的图像。 本题要求,给出一个图像的文字描述和一些关键字,请您计算有多少个关键字匹配。

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入说明

输入的第一行给出一个整数,表示有多少个测试用例。 每个测试用例首先给出整数N,表示关键字的数目;然后给出N个关键字(N<=10000),每个关键字只包含从'a''z'的字符,长度不超过50。 最后一行是图像的文字描述,长度不超过1000000

输出说明

输出给出在描述中包含了多少个关键字。

平台会对你编写的代码进行测试:

测试输入:

 
  1. 1
  2. 5
  3. she
  4. he
  5. say
  6. shr
  7. her
  8. yasherhs

预期输出: 3

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e7+5;
const int MAX=10000000;
int cnt;
struct node{                    //节点的结构类型
    node *next[26];             //序数值为i的子节点为next[i]
    node *fail;                 //匹配指针
    int sum;                    //匹配的模式数
};

node *root;                     //根
char key[70];                   //关键字
node *q[MAX];                   //队列
int head,tail;                  //队列的首位指针
node *newnode;                  //辅助节点
char pattern[maxn];             //图像的文字描述
int N;                          //关键字的数目
void Insert(char *s)            //将关键字s插入Trie树
{
    node *p=root;               //从根出发
    for(int i=0;s[i];i++)       //依次插入每个字符
    {
        int x=s[i]-'a';         //计算第i个字符的序数值
        if(p->next[x]==NULL)    //若p不存在序数值为x的子节点,则构造next[]域、sum域和fail域全0的子节点newnode,并将其设为p的序数值为x的子节点
        {
            newnode=(struct node*)malloc(sizeof(struct node));
            for(int j=0;j<26;j++)newnode->next[j]=0;
            newnode->sum=0;newnode->fail=0;
            p->next[x]=newnode;
        }
        p=p->next[x];            //从p的序数值为x的子节点继续搜索下去
    }
    p->sum++;                    //关键字的数目+1
}
void build_fail_pointer()        //设置Trie树的fail指针
{
    head=0;                      //队列的首尾指针初始化
    tail=1;
    q[head]=root;                //根入队
    node *p;                     //辅助节点p和temp
    node *temp;
    while(head<tail)             //若队列非空,则队首节点temp出队
    {
        temp=q[head++];
        for(int i=0;i<=25;i++)    //枚举每个字母的序数值
        {
            if(temp->next[i])     //若temp存在序数值为i的子节点
            {
                if(temp==root)    //若temp为根,则序数值为i的子节点的fail指针指向根
                {
                    temp->next[i]->fail=root;
                }
                else               //在temp为中间节点情况下,从temp出发,沿fail指针一直搜索至当前节点存在序数值为i的子节点
                {
                    p=temp->fail;
                    while(p)
                    {
                        if(p->next[i]) //若当前p节点存在序数值为i的子节点,则temp的序数值为i的子节点的fail指针指向p的序数值为i的子节点
                        {
                            temp->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;     //沿p的fail指针继续搜索下去
                    }
                    if(p==NULL)temp->next[i]->fail=root;  //若沿途没有任一节点存在序数值为i的子节点,则temp的序数值为i的子节点的fail指向根
                }
                q[tail++]=temp->next[i];    //temp的序数值为i的子节点入队
            }
        }
    }
}

void ac_automation(char *ch)        //对图像文字描述进行多关键字匹配
{
    node *p=root;                   //从根出发
    int len=strlen(ch);             //计算图像文字描述的长度
    for(int i=0;i<len;i++)          //依次匹配每个字符
    {
        int x=ch[i]-'a';            //计算第i个字符的序数值x
        while(!p->next[x]&&p!=root)p=p->fail;   //沿fail指针搜索下去,直至当前节点p存在序数值为x的子节点或者搜索至根
        p=p->next[x];               //将p的序数值为x的子节点设为p
        if(!p)p=root;               //若p不存在序数值为x的子节点,则根设为序数值为x的子节点
        node *temp=p;               //从序数值为x的子节点出发,继续沿fail指针搜索,直至搜索至根或者当前节点的sum域值为-1为止
        while(temp!=root)
        {
            if(temp->sum>=0)         //若当前节点的sum域值不小于0,则sum域值累计计入匹配的关键字数,当前节点的sum域值设为-1,以避免重复计算
            {
                cnt+=temp->sum;
                temp->sum=-1;
            }
            else break;               //当前节点的sum域值为-1,退出循环
            temp=temp->fail;          //继续沿fail指针搜索
        }

    }
}
int main()
{
    int T;
    scanf("%d",&T);                   //输入测试用例数
    while(T--)                        //依次处理每个测试用例
    {                                 //构造next[]域、fail域和sum域全0的根节点root
        root=(struct node*)malloc(sizeof(struct node));
        for(int j=0;j<26;j++)root->next[j]=0;
        root->fail=0;
        root->sum=0;
        scanf("%d",&N);                //读关键字数
        getchar();
        for(int i=1;i<=N;i++)          //依次读入每个关键字,构造Trie树
        {
            cin>>key;                  //读第i个关键字
            Insert(key);               //将该关键字插入Trie树
        }
        cin>>pattern;                  //读图像的文字描述
        cnt=0;                         //图像的文字描述中含关键字的数目初始化
        build_fail_pointer();          //设置Trie的树的fail指针
        ac_automation(pattern);        //对图像文字描述进行多关键字匹配
        printf("%d\n",cnt);            //输出图像文字描述中内含关键字的数目
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值