hihoCoder 1015 KMP算法(kmp)
#1015 : KMP算法
- 例子输入
-
5
HA
HAHAHA
WQN
WQN
ADA
ADADADA
BABABB
BABABABABABABABABB
DAD
ADDAADAADDAAADAAD - 例子输出
-
3
1
3
1 0 -
没什么好说的,模板题
-
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <iostream>
#include <string>
using namespace std;
int f[ 15000];
void getfill(string s)
{
memset(f,0,sizeof(f)); //依据其前一个字母得到
for(int i=1;i<s.size();i++)
{
int j=f[i];
while(j && s[i]!=s[j])
j=f[j];
f[i+1]=(s[i]==s[j])?j+1:0;
}
}
int find(string a,string s)
{
int ans=0;
getfill(s);int j=0;
for(int i=0;i<a.size();i++)
{
while(j && a[i]!=s[j])
j=f[j];
if(a[i]==s[j])
j++;
if(j==s.size()){
ans++;
}
}
return ans;
}
int main()
{
string s,a;
int T;
scanf("%d",&T);
while(T--)
{
getchar();
cin>>s>>a;
int ans=find(a,s);
printf("%d\n",ans);
}
return 0;
}
描写叙述
小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣。他们约定好互相帮助,在编程的学习道路上一同前进。
这一天。他们遇到了一仅仅河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不可以推断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?”
小Hi和小Ho细致思考了一下,认为仅仅能想到非常easy的做法,可是又认为既然河蟹先生这么说了,就肯定不会这么easy的让他们回答了。于是他们仅仅能说道:“抱歉,河蟹先生,我们仅仅能想到时间复杂度为(文本长度 * 特殊文字总长度)的方法,即对于每一个模式串分开推断,然后依次枚举起始位置并检查是否可以匹配,可是这不是您想要的方法是吧?”
河蟹点了点头,说道:”看来你们的水平还有待提高。这样吧。假设我说仅仅有一个特殊文字,你能不能做到呢?“
小Ho这时候还有点晕晕乎乎的,可是小Hi非常快开口道:”我知道!这就是一个非常经典的模式匹配问题!
能够使用KMP算法进行求解。“
河蟹惬意的点了点头,对小Hi说道:”既然你知道就好办了,你去把小Ho教会。下周我有重要的任务交给你们。“
”保证完毕任务。”小Hi点头道。
输入
第一行一个整数N。表示測试数据组数。
接下来的N*2行,每两行表示一个測试数据。
在每个測试数据中。第一行为模式串,由不超过10^4个大写字母组成。第二行为原串,由不超过10^6个大写字母组成。
当中N<=20
输出
对于每个測试数据。依照它们在输入中出现的顺序输出一行Ans。表示模式串在原串中出现的次数。
提示一:KMP的思路
小Hi和小Ho回到了学校,为了完毕河蟹委托的伟大使命,小Hi立刻把小Ho抓到了机房開始上课。
“小Ho,你来看看这样一段原串和模式串~”小Hi说着递上了一张纸条。
原串: | bababababababababb |
模式串: | bababb |
“嗯,这个样例中模式串bababb在原串中第13个字符開始的地方出现了”小Ho看了看。回答道。
“我们如果仍然使用最普通的方法来进行推断,即我们先枚举原串中的一个起始位置,然后推断从这个位置開始的字符串能否和模式串进行完匹配。”小HI说道,“然后我们来看这个过程中有没有什么能够缩减的计算量。”
“好的!”小Ho点点头。
“你看。在起始点为1的时候,匹配到第6个字符的时候发生了失败,这个时候我们应当做的是是不是将模式串右移一位。然后从头開始推断。就像这样?”小Hi又在纸上画了画,递给了小Ho。“
原串: | bababababababababb |
模式串: | bababb |
原串: | bababababababababb |
模式串: | bababb |
”是的,然后我们发现第一位就发现不能进行匹配。
“小Ho老老实实的回答。
”然后我们再将模式串右移一位,然后再从头開始推断。这次我们成功的越过了原串的第7个字符,在第8个字符产生了不同。
“小Hi继续往下推演。
原串: | bababababababababb |
模式串: | bababb |
”然后之后的剧情很的相似,都是要么最后一个字符匹配不成功,要么就是第一个字符就匹配不成功,一直到了最后一次机会的时候才匹配成功。
“小Ho做了总结。
”那你认为这个过程中有没有什么没有必要计算的呢?“小Hi于是问道。
”我是这么觉得的,你看这条线。“小Ho在两个串上对着的一个位置画了一条线。
原串: | babab | ababababababb |
模式串: | babab | b |
”嗯?”
“这是我们第一次产生了字符不匹配的情况。那么接下来的过程中一定会出现两种情况之中的一个:一种情况是模式串与原串的对齐点(即枚举的原串中的起点位置)越过了这条线,仍然没能匹配成功,而还有一种情况是原串中这个位置的字符与模式串中某个位置的字符匹配上了。”小Ho分析道:”我们先不考虑第一种情况,而来看看另外一种情况会发生什么。
“
原串: | babab | ababababababb |
模式串(对齐点=1): | babab | b |
模式串(对齐点=3): | bab | a |
”看不出嘛。小Ho你今天变成聪明了嘛!~”小Hi由衷的赞叹道。
“那当然,毕竟我近期在讨论区解答了非常多问题,这非常锻炼人的好么。“小Ho笑嘻嘻的回答道。
”那我也得表现下,接下来换我来说吧。反正你肯定也就差点儿相同想到这么多是吧!“小Hi也是看破了小Ho的底细。这般说道。于是小Ho点了点头,让小Hi接着说。
”我相信一个非常easy注意到的事实就在于,假设我用i表示原串和模式串产生分歧的位置(模式串上的位置。注意!
这个和对齐点是不一样的东西。一个在原串上。一个在模式串上),用j表示为了匹配掉位置i上产生分歧的字符而将模式串的对齐点移动到的位置。我们会发现,模式串[1, i-j]的这一段和[j, i - 1]这一段是同样的。比方在这个样例中i=6,j=3,我们会发现模式串[1, 3]和[3,5]是同样的。“小Hi整理了下思路。如是说道。
原串: | ba | bab | a babababababb |
模式串(i=1): | ba | bab | b |
模式串(i=3): | | bab | a |
”而我们同一时候也会发现,仅仅有在存在一个长度k,使得模式串[1, i-k]和[k, i-1]这两段同样的情况下,将模式串对其到位置k,才干保证原串和模式串的匹配过程可以进入到原串的位置i是否和模式串的相应字符同样的判定。在别的情况下,根本都进入不到位置i的推断就会发生不一致的情况了。”说着小Hi又抛出了另外一个命题。
“我已经開始有点晕了!”小Ho提出了抗议。
“那你就好好的读一遍我刚才说的话!然后自己在草稿纸上演算一下这个例子,非常快就能够得出结果的!”小Hi如是说道。”总而言之我们如今须要的一个数据是。这个长度k最长是多少,并且我们对于模式串的每个位置i,都要计算这个值。”而这就是KMP中最为重要的一个点——NEXT数组。
提示二:NEXT数组的使用
“那么。为了可以充分理解NEXT数组。我们再来回想一下怎样使用NEXT数组~"小Hi摆出一副老师的样子,说道。
”首先我们来给出NEXT数组的数学定义~“
NEXT[0] = -1
NEXT[i] = max{ 0<=k< i | str.substring(1, k) == str.substring(i - k +1 , i) } 当中str.substring(i, j)表示str从位置i到位置j的子串,假设i>j则,substring为空
”那么我们对之前样例中的模式串进行求解。能够得到这种NEXT数组。
“小Hi在纸上写了又写。画了又画。
模式串: | b a b a b b |
NEXT: | 0 0 1 2 3 1 |
”然后再来看这个NEXT数组是怎样使用的!为了表明NEXT的全部使用情况,我们换一个原串。然后首先,我们第一次匹配,假设用ori表示原串。用par表示模式串,用p表示原串的下标(从1開始)。用q表示模式串的下标(从1開始)的话。会发现最多匹配到p=5, q=5就不能往下匹配了。由于此时ori[p +1]不等于par[q + 1]“小Hi为了使说明更加简洁,先下了一堆定义。
”好的!
小Hi老师好棒!“小Ho在一旁煽风点火道。
原串(p=5): | babab | abcbababababb |
模式串(q=5): | babab | b |
”此时,令q = NEXT[q]。并将ori[1..p]和par[1..q]对齐。便会发现ori[1..p]和par[1..q]仍然是一一相应的。“
原串(p=5): | babab | abcbababababb |
模式串(q=3): | bab | abb |
“此时。ori[p+1]和par[q+1]同样了,于是可以继续往下匹配,可是到了p=7,q=5的时候又发现不可以接着匹配了。”
原串(p=7): | bababab | cbababababb |
模式串(q=5): | babab | b |
”此时。令q = NEXT[q],并将ori[1..p]和par[1..q]对齐,便会发现ori[1..p]和par[1..q]仍然是一一相应的,这和之前是一样的。
”
原串(p=7): | bababab | cbababababb |
模式串(q=3): | bab | abb |
“此时,ori[p+1]和par[q+1]仍然不同样。于是还得令q=NEXT[q]。”
原串(p=7): | bababab | cbababababb |
模式串(q=1): | b | ababb |
“此时,ori[p+1]和par[q+1]仍然不同样。令q=NEXT[q]。”
原串(p=7): | bababab | cbababababb |
模式串(q=0): | | bababb |
“此时,ori[p+1]和par[q+1]仍然不同样,令q=NEXT[q]。”
原串(p=7): | bababab | cbababababb |
模式串(q=-1): | | bababb |
”到了这一步,就相当于我们之前所说的模式串与原串的对齐点(即枚举的原串中的起点位置)越过了这条线(当时指C右側的那条线)的情况,这样的情况下,就应当p和q均+1,然后继续之前的操作。”小Hi擦了一把汗,说道。
“这样一说,我就大致可以理解NEXT数组是怎么用来求解模式匹配问题的了,可是它是怎样求的呢?一般的方法不是要O(模式串长度的立方)的么?”小Ho问道。
“这就是我接下来要和你说的啦!”小Hi笑道:“可是让我先喝口水!”
提示三:怎样求解NEXT数组
“首先我们不想怎样求整个NEXT数组,而是如果我们已经知道了之前样例中模式串的NEXT[1..4]。来求NEXT[5]怎样?”小Hi建议道。
“好的!
这样我们就仅仅须要平方级的算法就能够算出它的值了。”小Ho高兴道。
“有点追求好不好!”小Hi深深的吸了一口气:“你这样和之前的解法有什么不同么。”
“似乎没有。
。那你说怎么算吧!
我反正脑子已经成浆糊了。
”小Ho郁闷道。
“我们把par.substring(1, 5)当做新的原串ori_new,然后把par.substring(1, 4)当做新的模式串par,会怎样?”小Hi微微一笑。
“会。。我来试试。"小Ho接过小Hi手中的纸笔,便開始演算:“首先就直接匹配到了p=4, q=4的情况,这时候严格来说已经算匹配完毕了,可是肯定不是就这么结束的,此时par_new[q +1]由于是空字符,所以肯定和ori_new[p+1]匹配不上。于是令q = NEXT[q]”
原串(p=4): | baba | b |
模式串(q=4): | baba | |
原串(p=4): | baba | b |
模式串(q=2): | ba | b |
”然后这时候ori_new[p + 1]就直接和par_new[q + 1]匹配上了,于是新的p=5。q=3,莫非……这个最后的q就是NEXT[5]!“小Ho忽然灵光一闪。
”没错。就是这样!那你想想如今怎样求NEXT[6]。
“小Hi继续引导小Ho。
”首先我们没有必要又一次从头開始匹配,直接在原串和模式串的后面加上第6个字符就能够了。“小Ho分析道。
原串(p=5): | babab | b |
模式串(q=3): | bab | abb |
”没法继续匹配,于是令q=NEXT[q]。“
原串(p=5): | babab | b |
模式串(q=1): | b | ababb |
”还是没法继续匹配,于是令q=NEXT[q]。“
原串(p=5): | babab | b |
模式串(q=0): | | bababb |
”此时能够匹配了。新的p=6,q=1,所以NEXT[6]就是1。“小Ho高兴道:”没想到NEXT数组的本身会用一种递归的方式进行求解,真是太巧妙了!“
”那你要不要赶紧去写一下代码,KMP算法的代码但是能够写的非常短非常巧妙的哦!
~“小Hi建议道。
”好!“
hihoCoder 1015 KMP算法(kmp)的更多相关文章
- KMP算法 KMP模式匹配 一(串)
A - KMP模式匹配 一(串) Crawling in process... Crawling failed Time Limit:1000MS Memory Limit:131072KB ...
- 【模式匹配】KMP算法的来龙去脉
1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串\(T\)中是否出现该模式串\(P\),即\(P\)为\(T\)的子串.特别地,定义主串为\(T[0 \dots n-1]\),模 ...
- 字符串匹配算法——KMP算法
处理字符串的过程中,难免会遇到字符匹配的问题.常用的字符匹配方法 1. 朴素模式匹配算法(Brute-Force算法) 求子串位置的定位函数Index( S, T, pos). 模式匹配:子串的定位操 ...
- 模式串匹配之KMP算法
模式串匹配之KMP算法 KMP算法 模式值计算(next[j]) (1) next[0]=-1, 第一个字符模式值为-1 (2) next[j]=-1, T中下标为j的字符与首字符相同,且j前面的1 ...
- 浅析KMP算法
浅析KMP算法 KMP算法是一种线性字符串的匹配算法,将主串S与模式串T匹配. 首先朴素算法大家都会,就是直接从S的每一个位置开始,枚举比较,时间效率为O(nm),现在要想到一种化简的方式,使得时间复 ...
- KMP算法(转)
KMP算法 在介绍KMP算法之前,先介绍一下BF算法. 一.BF算法 BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个 ...
- KMP算法(研究总结,字符串)
KMP算法(研究总结,字符串) 前段时间学习KMP算法,感觉有些复杂,不过好歹是弄懂啦,简单地记录一下,方便以后自己回忆. 引入 首先我们来看一个例子,现在有两个字符串A和B,问你在A中是否有B,有几 ...
- KMP算法的来龙去脉
1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串TT中是否出现该模式串PP,即PP为TT的子串.特别地,定义主串为T[0-n−1]T[0-n−1],模式串为P[0-p−1]P[0 ...
- 什么是KMP算法?KMP算法推导
花了大概3天时间,了解,理解,推理KMP算法,这里做一次总结!希望能给看到的人带来帮助!! 1.什么是KMP算法? 在主串Str中查找模式串Pattern的方法中,有一种方式叫KMP算法 KMP算法是 ...
随机推荐
- 百科知识 英特尔处理器I5 4460和4590有哪些区别
4460是855元 4590是880元 i5 4460与4590CPU主要区别在:1.主频差0.3GHz;;2.最大睿频相差0.5GHz;:3.核显(HD4600)最大动态频率相差0.1GHz ...
- Linux中Centos7下安装Mysql(更名为Mariadb)
一.安装: yum install mariadb-server mariadb 二.启动服务: systemctl start mariadb 三.配置大小写敏感问题.和字符为utf8: vim / ...
- JAVA的PreparedStatement和addBatch()方法
本文介绍两个内容,为什么使用PreparedStatement的addBatch()方法?以及使用PreparedStatement的好处. 一.addBatch使用方法 昨天用JAVA做了一个导表的 ...
- claim概念图示
- httpclient Accept-Encoding 乱码
解决方法 HttpEntity httpEntity = httpResponse.getEntity(); if (httpEntity != null) { if (httpEntity.getC ...
- SSH协议
SSH是一种协议,实现计算机之间的加密登录,即使被截获,截获的也只是加密后的密文,不会泄密. 如果每次登录另外一台计算机,都需要输入密码,就显得太麻烦,所以SSH协议实现了无密码登录,即公钥登录.所谓 ...
- 算法笔记_221:串的简单处理(Java)
目录 1 问题描述 2 解决方案 1 问题描述 串的处理在实际的开发工作中,对字符串的处理是最常见的编程任务.本题目即是要求程序对用户输入的串进行处理.具体规则如下:1. 把每个单词的首字母变为大 ...
- VB数组的清除
在一个程序中,同一数组只能用Dim语句定义一次.但有时可能需要清除数组的内容或对数组重新定义,这可以用:Erase语句来实现. 格式:Erase(数组名)[,(数组名)] 功能:用于重新初始化静态数组 ...
- OpenCV学习代码记录——轮廓(contour)检测
很久之前学习过一段时间的OpenCV,当时没有做什么笔记,但是代码都还在,这里把它贴出来做个记录. 代码放在码云上,地址在这里https://gitee.com/solym/OpenCVTest/tr ...
- 转 linux Crontab 使用
cron用法说明 cron的用法老是记不住,索性写下来备忘.下文内容大部分是根据<Cron Help Guide>翻译而来,有些部分是自己加上的. 全文如下: cron来源于希腊单词chr ...