您的位置:首页 > 其它

#1015 : KMP算法

2015-09-04 18:15 288 查看
时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?”小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就肯定不会这么容易的让他们回答了,于是他们只能说道:“抱歉,河蟹先生,我们只能想到时间复杂度为(文本长度 * 特殊文字总长度)的方法,即对于每个模式串分开判断,然后依次枚举起始位置并检查是否能够匹配,但是这不是您想要的方法是吧?”河蟹点了点头,说道:”看来你们的水平还有待提高,这样吧,如果我说只有一个特殊文字,你能不能做到呢?“小Ho这时候还有点晕晕乎乎的,但是小Hi很快开口道:”我知道!这就是一个很经典的模式匹配问题!可以使用KMP算法进行求解!“河蟹满意的点了点头,对小Hi说道:”既然你知道就好办了,你去把小Ho教会,下周我有重要的任务交给你们!“”保证完成任务!”小Hi点头道。

输入

第一行一个整数N,表示测试数据组数。接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。其中N<=20

输出

对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。样例输入
5
HA
HAHAHA
WQN
WQN
ADA
ADADADA
BABABB
BABABABABABABABABB
DAD
ADDAADAADDAAADAAD
样例输出

3
1
3
1
0
提示和题目连接:http://hihocoder.com/problemset/problem/1015
解答:
通过查阅资料发现了四种进行字符串匹配的算法,分别为朴素算法、Rabin-Karp、有限自动机算法、Knuth-Morris-Pratt算法
设定已知条件:已知模式P[1..m]需要匹配的文本T[1..n]
首先说朴素算法,朴素算法真的很朴素,就是通过一个循环来寻找有效的移位,该循环对n-m+1个可能的每一个移位值s进行匹配检查P[1..m]=T[s+1..s+m]
算法伪码:
NAIVE-STRING-MATCHER(T,P)
n  <- length[T]
m <- length[P]
for  s  <- 0 to n-m
do if P[1..m] = T[s+1..s+m]
then print"找到一个匹配的模式"
下面暂时略过Rabin-Karp算法,回头补上,有限自动机算法:  这个算法的步骤就是根据模式P生成一个状态变迁函数,也可以绘出一个状态变迁图例如模式P = {ababaca}就是可以定义7个状态注:对于长度为n的字符串模式,一般定义n+1个状态,其中包含一个初始状态和一个接受状态,在接受状态意味着一个匹配的成功。继续上面模式P的变迁图建立:  这个是对应于模式P={ababaca}的状态转换图,图中浅蓝的标号0的点为起始状态,红色的标号为0的点为接受状态,图中未画出的输入对应的线均指向状态0.  在状态0位置,输入a时,此时字符串a的最长后缀对应P的最长前缀字符串a,长度为1,状态转移到1;输入b时,对应的最长前缀字符串长度为0,c时也是  在状态1位置,输入a时,此时字符串aa的最长后缀对应P的最长前缀字符串为a,长度为1,状态转移到1;输入b时,此时字符串ab的最长后缀对应P的最长前缀字符串为ab,长度为2,所以状态转移到2;当输入c时,字符串ac的最长后缀对应P的最长前缀字符串为空,长度为0,状态转移到0。  ......  在状态7位置,到达接受状态,即此时已经出现了一次成功匹配,开始下一轮,再输入一个字符a时状态转移到1,输入b时状态转移到2上述即为有限状态机的建立过程,由此可知,当对应模式P的有限状态机建立后,对于文本T的判断是线性的,只需要对于每一个字符输入后的状态变迁,每次到达接受状态就完成一次成功的匹配。KMP算法:  KMP算法的关键在于找到模式P的前缀函数next。  在此以模式P={ababababca}为例,阐述一下KMP前缀函数的建立意义。考察朴素的字符串匹配算法的操作过程,当上述模式当中前四个字符匹配成功后,如果第五个字符匹配失败,说明第文本中对应的第五个字符不是a(建议在纸上画一下),还说明了对应的四个字符为abab,将P右移一个位置发现仍然不匹配,右移两个、三个、四个也是,但是右移五个未必。因此能否不像上述方法那样一步一步右移,而是直接右移五个位置开始进行判断。其实在上面匹配过程当中,每次成功的匹配就包含了一定量的信息,而分析模式当中ab的重复也可给人以匹配失败后,可否每次移动两个位置再进行匹配的启发,这样充分发掘模式本身的特点可以建立一个next函数,从而确定每次匹配失败后移动的长度。对于上述模式P发掘的next函数为伪代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;

int KMP(string t, string p){
int n = p.size();
vector <int> next(n+1, 0);
for(int i=1; i<n; i++){
int j=i;
while(j > 0){
j = next[j];
if(p[j] == p[i]){
next[i+1] = j + 1;
break;
}
}
}

int ans = 0;
int m = t.size();
for(int i=0, j=0; i<m; i++){
if(j < n && t[i] == p[j])  j++;
else{
while(j > 0){
j = next[j];
if(t[i] == p[j]){
j++;
break;
}
}
}
if(j == n) 	ans ++;
}
return ans;
}

int main(){
string t, p;
int n;
scanf("%d", &n);
while(n--){
cin>>p>>t;
cout<<KMP(t, p)<<endl;
}
return 0;
}
  

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: