Keywords Search HDU - 2222(AC自动机模板)

题意:

给定 n个长度不超过 50的由小写英文字母组成的单词准备查询,以及一篇文章,问:文中出现了多少个待查询的单词。多组数据。

题目:

In the modern time, Search engine came into the life of everybody like Google, Baidu, etc.
Wiskey also wants to bring this feature to his image retrieval system.
Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched.
To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match.
Input
First line will contain one integer means how many cases will follow by.
Each case will contain two integers N means the number of keywords and N keywords follow. (N <= 10000)
Each keyword will only contains characters ‘a’-‘z’, and the length will be not longer than 50.
The last line is the description, and the length will be not longer than 1000000.
Output
Print how many keywords are contained in the description.
Sample Input
1
5
she
he
say
shr
her
yasherhs
Sample Output
3

分析:

此题为AC自动机模板匹配多模式串问题。单模式串匹配用KMP算法,那么多模式串匹配,不可能一个一个字符串建立失配数组,复杂度太高0(m*n),所以对多模式串建成一个trie树,再建立失配数组,复杂度为o(nn\sqrt{n}n)。AC自动机详细分析见代码后。

AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using  namespace std;
const int N=5e5+10;
int ans,tot,dp[N],ch[N][30],bo[N],que[N];
void makes(char *s)/**将所有模式串构建成一颗tire树*/
{int u=1,len=strlen(s);for(int i=0; i<len; i++){int c=s[i]-'a';if(!ch[u][c])ch[u][c]=++tot;u=ch[u][c];}bo[u]++;return ;
}
void bfs()/**通过BFS构建dp(失配)数组*/
{for(int i=0; i<26; ++i)ch[0][i]=1;/**为了方便将0的所有转移边都设为根节点1*/que[1]=1,dp[1]=0;/**若在根节点失配,则无法匹配字符*/for(int x=1,y=1; x<=y; x++){int u=que[x];for(int i=0; i<=25; i++){if(!ch[u][i])/**如果不存在u的转移边i时,失配,KMP*/ch[u][i]=ch[dp[u]][i];///优化else{que[++y]=ch[u][i];/**若有这个儿子则将其加入队列中*/dp[ch[u][i]]=ch[dp[u]][i];}}}
}
void find(char *s)
{int u=1,len=strlen(s),c,k;for(int i=0; i<=len; i++){c=s[i]-'a';k=ch[u][c];while(k>1){ans+=bo[k];bo[k]=0;k=dp[k];}u=ch[u][c];}return ;
}
int main()
{int t,n;char s[N<<1];scanf("%d",&t);while(t--){ans=0,tot=1;memset(bo,0,sizeof(bo));memset(ch,0,sizeof(ch));scanf("%d",&n);while(n--){scanf("%s",s);makes(s);}bfs();scanf("%s",s);find(s);printf("%d\n",ans);}return 0;
}

AC自动机详细分解:

ac自动机很神奇,在于这个算法中失配指针的妙处(好比kmp算法中的next数组),说它高深,是因为这个不是一般的算法,而是建立在两个普通算法的基础之上,而这两个算法就是kmp与字典树。ac自动机其实就是一种多模匹配算法,那么你可能会问什么叫做多模匹配算法。下面是我对多模匹配的理解,与多模与之对于的是单模,单模就是给你一个单词,然后给你一个字符串,问你这个单词是否在这个字符串中出现过(匹配),这个问题可以用kmp算法在比较高效的效率上完成这个任务。那么现在我们换个问题,给你很多个单词,然后给你一段字符串,问你有多少个单词在这个字符串中出现过,当然我们暴力做,用每一个单词对字符串做kmp,这样虽然理论上可行,但是时间复杂度非常之高。

建立tire树(就是tire树模板)这个简单

void makes(char *s)/**将所有模式串构建成一颗tire树*/
{int u=1,len=strlen(s);for(int i=0; i<len; i++){int c=s[i]-'a';if(!ch[u][c])ch[u][c]=++tot;u=ch[u][c];}bo[u]++;return ;
}

建立后此时多模式串为一个字典树,将存在公共前缀的字符串“合并”,可以用一个数组(每个新点插入++tot)表示树上所有点。

建一个树的图:

在这里插入图片描述

建立该字典树的失配数组(bfs(队列)+KMP+前缀数组)重点

void bfs()/**通过BFS构建dp(失配)数组*/
{for(int i=0; i<26; ++i)ch[0][i]=1;/**为了方便将0的所有转移边都设为根节点1*/que[1]=1,dp[1]=0;/**若在根节点失配,则无法匹配字符*/for(int x=1,y=1; x<=y; x++){int u=que[x];for(int i=0; i<=25; i++){if(!ch[u][i])/**如果不存在u的转移边i时,失配,KMP*/ch[u][i]=ch[dp[u]][i];///优化else{que[++y]=ch[u][i];/**若有这个儿子则将其加入队列中*/dp[ch[u][i]]=ch[dp[u]][i];}}}
}
  • 首先建立的是一个tire树,一个结点上可能连多个节点,所以这里用BFS(用进入队列的方式,处理每一个点),最后将tire树建立成一个可与给定串匹配的失配数组
  • 构建失配数组(在代码中我写的是dp数组),使当前字符失配时跳转到另一段(最近匹配的位置)【root开始每一个字符(前缀)都与当前已匹配字符段某一个后缀完全相同且长度最大的位置继续匹配】如同KMP算法一样,AC自动机在匹配时如果当前字符串匹配失败,那么利用失配指针进行跳转。由此可知如果跳转,跳转后的串的前缀必为跳转前的模式串的后缀,并且跳转的新位置的深度(匹配字符个数)一定小于跳之前的节点(跳转后匹配字符数不可能大于跳转前,否则无法保证跳转后的序列的前缀与跳转前的序列的后缀匹配)。
  • 因为是一个字典树(用ch数组表示,是一个二维数组,ch[u][c]),由于字典树c就为当前点++tot,u为上一个点,ch[u][c]就为当前点是否为树上的第几个点。 dp[ch[u][i]](等同于dp[i++] 由tire树ch[u][c]==++tot(将字典树上的某一个链看做需要匹配的字符串) ) =ch[dp[u]][i](等同于j++(上次匹配位置的++tot)) ;
  • 显然我们在构建失配数组的时候都是从当前节点的父节点的失配数组出发,由于Trie树将所有单词中相同前缀压缩在了一起,所以所有失配数组都不可能平级跳转(到达另一个与自己深度相同的节点),因为如果平级跳转,很显然跳转所到达的那个节点肯定不是当前匹配到的字符串的后缀的一部分,否则那两个节点会合为一个,所以跳转只能到达比当前深度小的节点,又是由当前节点(ch[u][c]==++tot )父节点(u )开始的跳转,所以这样就可以保证从root到所跳转到位置的那一段字符串长度小于当前匹配到的字符串长度。另一方面,我们可以类比KMP求NEXT数组时求最大匹配数量的思想,那种思想在AC自动机中的体现就是当构建失配数组时不断地回到之前的跳转位置,然后判断跳转位置的下一个字符是否包含当前字符,如果是就将失配数组与那个跳转位置连接,如果跳转位置指向tire树上的空点就说明当前匹配的字符在当前深度之前没有出现过,无法与任何跳转位置匹配,而若是找到了第一个跳转位置的下一个字符包含当前字符的的跳转位置,则必然取到了最大的长度,这是因为其余的当前正在匹配的字符必然在第一个跳转位置的下一个字符包含当前字符的的跳转位置深度之上,而那样的跳转位置就算可以,也不会是最大的(最后一个字符的深度比当前找到的第一个可行的跳转位置的最后一个字符的深度小,串必然更短一些)。
    if(!ch[u][i])/**如果不存在u的转移边i时,失配,KMP*/ch[u][i]=ch[dp[u]][i];///优化(即找到后缀完全相同且长度最大的位置)
  • 如图:
    在这里插入图片描述

查询操作(与字符串匹配)

void find(char *s)
{int u=1,len=strlen(s),c,k;for(int i=0; i<=len; i++){c=s[i]-'a';k=ch[u][c];while(k>1){ans+=bo[k];bo[k]=0;k=dp[k];}u=ch[u][c];}return ;
}
  • 如果节点匹配,就一直进行下去,每次都加每个节点的bo[u],并初始化为0,但只有代表单词结尾的字符bo[u]才是1,其他的为0.所以才有
   while(k>1){ans+=bo[k];bo[k]=0;k=dp[k];}

例如,在查询过程中,匹配字符为abcdef,若有一个模式串为bc,那么因为KMP只扫过前缀,就会忽略导致结果错误,所以每次让某匹配点失配,看匹配串的后缀和之前匹配的前缀是否存在一模式串(bo[u]!=0)到空点结束.

  • 该查询默认一直匹配,因为在构造失配数组时进行了优化,见下代码
 if(!ch[u][i])/**如果不存在u的转移边i时,失配,KMP*/ch[u][i]=ch[dp[u]][i];///优化(即找到后缀完全相同且长度最大的位置)

导致一旦失配,就会有点进行补充(前面构造失配数组做的优化操作)。
直到需要查询的串走完。
(总的来说,算法比较神奇,但是可以直接套KMP模板,简单来说,失配数组的作用就是将主串某一位之前的所有可以与模式串匹配的单词快速在Trie树中找出。)

俗话说言多必失,更不要说我还是一个蒟蒻 QAQ 这么长的理解,一定有地方有些小失误,欢迎大犇来指正orz。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

介绍一个基于 .NET 的船的新 PHP SDK + Runtime: PeachPie

前言这几天想基于 .NET Core 搞一个自己的博客网站&#xff0c;于是在网上搜刮各种博客引擎&#xff0c;找到了这些候选&#xff1a;Blogifier、Miniblog 以及 edi 写的 Moonglade。Blogifier&#xff1a;这是前端是个 Angular SPA 应用&#xff0c;不利于 SEO&#xff0c;同时…

[设计模式]适配器模式

适配器模式:将一个类的接口转换成客户希望的另外一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 &#xff08;将已经写好的&#xff0c;但是不符合需求的接口&#xff0c;转换成目标接口&#xff09; 代码如下: #include <iostream>…

[工具]微软的学习平台Microsoft Learn很好用,推荐一下

1. 什么是Microsoft LearnMicrosoft Learn是微软这两年大力推广的全新学习平台&#xff0c;可提供 Microsoft 产品交互式学习体验。基本上无需登录即可使用&#xff0c;但登录后可以使用更多功能&#xff0c;包括&#xff1a;累积分数和成就跟踪学习活动进度使用免费的 Azure 资…

多终端数据同步机制设计

多终端数据同步机制设计之前写过一篇文章数据同步流程设计的文章&#xff0c;这里整理一下在公众号里分享一下Intro因为项目需要&#xff0c;需要设计一个多终端数据同步的机制&#xff0c; 需要满足以下条件&#xff1a;多个终端数据操作及同步&#xff0c;终端可能离线每次同…

Popular Cows POJ - 2186(tarjan算法)+详解

题意&#xff1a; 每一头牛的愿望就是变成一头最受欢迎的牛。现在有 N头牛&#xff0c;给你M对整数&#xff08;A,B&#xff09;&#xff0c;表示牛 A认为牛B受欢迎。这种关系是具有传递性的&#xff0c;如果 A认为 B受欢迎&#xff0c; B认为 C受欢迎&#xff0c;那么牛 A也认…

[设计模式]装饰模式

装饰模式: 通过AbstractEquipment装饰AbstractHero&#xff0c;使其heroA增加了一个穿装备的功能。 代码如下: #include <iostream> using namespace std;class AbstractHero {public:virtual void showStatus() 0;int hp;int mp;int at;int df; };class HeroA :publi…

ASP.NET Core分布式项目实战(Consent Controller Get请求逻辑实现)--学习笔记

任务20&#xff1a;Consent Controller Get请求逻辑实现接着上一节的思路&#xff0c;实现一下 ConsentController根据流程图在构造函数注入 IClientStore&#xff0c;IResourceStore&#xff0c;IIdentityServerInteractionService构造函数private readonly IClientStore _cli…

[设计模式]观察者模式

代码如下: #include <iostream> #include <list> using namespace std;class AbstractHero { public:virtual void update() 0; };class HeroA :public AbstractHero { public:HeroA(){cout << "英雄A正在打BOSS" << endl;}virtual void u…

RMQ算法讲解

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 本文链接&#xff1a;https://blog.csdn.net/qq_41311604/article/details/79900893 </div><!--一个博主专栏付费入口--><!--一个…

Kubernetes是容器化微服务的圣杯么?

导语Kubernetes已成为山丘之王。开源技术Kubernetes以及随后的发行版正以超快的速度让人们爱上容器技术&#xff0c;并且开始夺回对容器化环境的控制权。不幸的是&#xff0c;编排容器只是战斗进行了一半。正文云服务提供商接连宣布他们的编排选择是Kubernetes私有发行版&#…

[设计模式]命令模式

代码如下: #include <iostream> #include <queue> #include <Windows.h> using namespace std;class HandleClientProtocol { public:void addMoney(){cout << "给玩家增加金币" << endl;}void addDiamond(){cout << "给玩…

Zjnu Stadium HDU - 304 加权并查集

题意&#xff1a; 观众席围成一圈。列的总数是300&#xff0c;编号为1–300&#xff0c;顺时针计数&#xff0c;我们假设行的数量是无限的。将有N个人去那里。他对这些座位提出了要求&#xff1a;这意味着编号A的顺时针X距离坐着编号B。例如&#xff1a;A在第4列&#xff0c;X…

还不明白可空类型原理? 我可要挖到底了

一&#xff1a;背景1. 讲故事做好自媒体到现在有一个月了&#xff0c;关注我的兄弟应该知道我产出了不少文章&#xff0c;号里的粉丝也多起来了&#xff0c;我也尽最大努力做到有问必回&#xff0c;现在是基础的、高深的问题都接踵而来&#xff0c;可我也只是一只小菜鸟&#x…

[设计模式]策略模式

策略模式:定义了一系列算法&#xff0c;并将每一个算法封装起来&#xff0c;而且使它们还可以相互替换。 策略模式让算法独立于使用它的客户而独立变化。 代码如下: #include <iostream> using namespace std;class WeaponStrategy { public:virtual void useWeapon()…

[设计模式]模板方法模式

模板方法模式: 定义一个操作中算法的框架&#xff0c;而将一些步骤延迟到子类中。模仿方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 代码如下: #include <iostream> using namespace std;class DrinkTemplate { public:virtual void Boi…

差距(分享)

非985大学生, 你和别人的差距在哪里?&#xff08;转&#xff09; 非985大学生, 你和别人的差距在哪里? 中国青年报03-24 在知乎上看到这样一段话&#xff1a; “渣学校意味着渣教学&#xff0c;渣教学意味着渣学历&#xff0c;渣学历意味着渣就业&#xff0c;就算以后考了研究…

在微服务框架Demo.MicroServer中添加SkyWalking+SkyApm-dotnet分布式链路追踪系统

1.APM工具的选取Apm监测工具很多&#xff0c;这里选用网上比较火的一款Skywalking。Skywalking是一个应用性能监控(APM)系统&#xff0c;Skywalking分为服务端Oap、管理界面UI、以及嵌入到程序中的探针Agent部分&#xff0c;大概工作流程就是在程序中添加探针采集各种数据发送给…

计算机组成原理期末复习题

地址总线A15~Ao(低),存储空间(按字节编址)分配如下 2000H~3FFFH为ROM区, 5000H~6FFFH为RAM区。用 ROM芯片(4Kx4)和RAM芯片(4Kx4)组成该存储器。请回答 &#xff1a;(1)分别需要ROM和RAM多少片? (2)用二进制形式写出每组芯片的地址范围,并说明可以通过哪些地址位来形成片选信号…

干货分享:如何使用Kubernetes的Ingress API

导语以Kubernetes的Kong为例&#xff0c;聊聊当前流行的开源且与云无关的Ingress控制器。正文您可以通过使用诸如Kong for Kubernetes的Ingress控制器&#xff08;使用自定义资源定义并提供许多插件&#xff09;来极大地扩展Ingress资源的功能。Kubernetes正在整个技术行业中得…

计算机组成原理期末复习往年卷子

1. I/O设备的编址方式通常有___统一编址__和_独立编址__两种方式。P145 2&#xff0e;Cache是一种高速缓冲存储器&#xff0c;是为了解决____CPU____和___主存____之间速度不匹配而采用的一项重要技术。P124 3&#xff0e;在计算机系统中&#xff0c;I/O设备与主机传递消息的…