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,一经查实,立即删除!

相关文章

php fpm 调试模式,调试 – nginx php-fpm xdebug netbeans只能启动一个调试会话

在过去,我使用apache mod_PHP xdebug netbeans进行开发我的网站(服务器是我的本地机器,运行Debian Squeeze),很高兴 – xdebug工作正常,调试会话可以随时启动和停止,当我需要时它.但是,当我转移到Nginx PHP_fpm xdebug netbeans时,我遇到了一些调试问题.>我的调试会话可能会…

介绍一个基于 .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>…

数位dp总结 之 从入门到模板(stO)

#转载自https://blog.csdn.net/wust_zzwh/article/details/52100392 基础篇 数位dp是一种计数用的dp&#xff0c;一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp&#xff0c;字面意思就是在数位上进行dp咯。数位还算是比较好听的名字&#xff0c;数位的含义…

matlab极大无关组,matlab最大无关组

与《matlab最大无关组》相关的范文课程设计任务书 2011-2012学年第一学期 专业: 通信工程 学号: 姓名: 课程设计名称: 信息论与编码课程设计 设计题目: 对称信道容量的求解 完成期限:自 2011 年 12 月 19 日至 2011年 12 月 25 日共 1 周 一&#xff0e;设计目的 1.深刻理解信道…

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

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

[PAT乙级]1030 完美数列

给定一个正整数数列&#xff0c;和正整数 p&#xff0c;设这个数列中的最大值是 M&#xff0c;最小值是 m&#xff0c;如果 M≤mp&#xff0c;则称这个数列是完美数列。 现在给定参数 p 和一些正整数&#xff0c;请你从中选择尽可能多的数构成一个完美数列。 输入格式&#xf…

php抓取多个网页合并,PHP 使用 CURL 同步抓取多个网页

一般CURL 抓网页的方法&#xff0c; 是一页一页抓&#xff0c; 假设要抓 4页&#xff0c; 所费时间各别是 5,10,7,5 秒&#xff0c; 那全部总合所花的时间就是 5 10 7 5 27 秒。若能同时间去抓取多个网页&#xff0c; 所花费的时间 5,10,7,5 秒&#xff0c; 全部总合所花的…

多终端数据同步机制设计

多终端数据同步机制设计之前写过一篇文章数据同步流程设计的文章&#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…

oracle导出BOM文件,ORACLE ERP导数据(BOM清单)-备份恢复-Oracle频道-中国IT实验室

方法&#xff1a;把数据导入BOM清单的方法是&#xff0c;把数据导入接口表中&#xff0c;让其自动运行既可。上传文件的时候&#xff0c;要注意使 用ASCII字符模式。1、自己建立一中转表drop table cux_bill_temp;create table cux_bill_temp(bill_sequence_id number,as…

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 << "给玩…

oracle 附加日志 挂起,Oracle 附加日志(supplemental log)

附加日志(supplemental log)可以指示數據庫在日志中添加額外信息到日志流中&#xff0c;以支持基於日志的工具&#xff0c;如邏輯standby、streams、GoldenGate、LogMiner。可以在數據庫和表上設置。1.數據庫級設置&#xff0c;分兩類&#xff1a;1.1最小附加日志(minimal supp…

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…