KMP算法 (字符串的匹配)
对于正常的字符串模式匹配,主串长度为m,子串为n,时间复杂度会到达O(m*n),而如果用KMP算法,复杂度将会减少线型时间O(m+n)。
设主串为ptr="ababaaababaa";,要比较的子串为a=“aab”;
KMP算法用到了next数组,然后利用next数组的值来提高匹配速度,我首先讲一下next数组怎么求,之后再讲匹配方式。
next数组详解
首先是理解KMP算法的第一个难关是next数组每个值的确定,这个问题困恼我很长时间,尤其是对照着代码一行一行分析,很容易把自己绕进去。
定义一串字符串
ptr = "ababaaababaa";
next[i](i从1开始算)代表着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的个数。
什么是前缀?
在“aba”中,前缀就是“ab”,除去最后一个字符的剩余字符串。
同理可以理解后缀。除去第一个字符的后面全部的字符串。
在“aba”中,前缀是“ab”,后缀是“ba”,那么两者最长的子串就是“a”;
在“ababa”中,前缀是“abab”,后缀是“baba”,二者最长重复子串是“aba”;
在“abcabcdabc”中,前缀是“abcabcdab”,后缀是“bcabcdabc”,二者最长重复的子串是“abc”;
这里有一点要注意,前缀必须要从头开始算,后缀要从最后一个数开始算,中间截一段相同字符串是不行的。
再回到next[i]的定义,对于字符串ptr = "ababaaababaa";
next[1] = -1,代表着除了第一个元素,之前前缀后缀最长的重复子串,这里是空 ,即"",没有,我们记为-1,代表空。(0代表1位相同,1代表两位相同,依次累加)。
next[2] = -1,即“a”,没有前缀与后缀,故最长重复的子串是空,值为-1;
next[3] = -1,即“ab”,前缀是“a”,后缀是“b”,最长重复的子串“”;
next[4] = 1,即"aba",前缀是“ab”,后缀是“ba”,最长重复的子串“a”;next数组里面就是最长重复子串字符串的个数
next[5] = 2,即"abab",前缀是“aba”,后缀是“bab”,最长重复的子串“ab”;
next[6] = 3,即"ababa",前缀是“abab”,后缀是“baba”,最长重复的子串“aba”;
next[7] = 1,即"ababaa",前缀是“ababa”,后缀是“babaa”,最长重复的子串“a”;
next[8] = 1,即"ababaaa",前缀是“ababaa”,后缀是“babaaa”,最长重复的子串“a”;
next[9] = 2,即"ababaaab",前缀是“ababaaa”,后缀是“babaaab”,最长重复的子串“ab”;
next[10] = 3,即"ababaaaba",前缀是“ababaaab”,后缀是“babaaaba”,最长重复的子串“aba”;
next[11] = 4,即"ababaaabab",前缀是“ababaaaba”,后缀是“babaaabab”,最长重复的子串“abab”;
next[12] = 5,即"ababaaababa",前缀是“ababaaabab”,后缀是“babaaaababa”,最长重复的子串“ababa”;
还有另外一种方法,我看的有的书上写着:
这里我们定义next[1] = 0 , next[1] = 1;
再分析ptr字符串,ptr = "ababaaababaa";
跟上一个的情况类似,
next[1] = 0 ,事先定义好的
next[2] = 1 ,事先定义好的
next[3] = 1 ,最长重复的子串“”;1代表没有重复,2代表有一个字符重复。
next[4] = 2 ,最长重复的子串“a”;追偿的长度加1,即为2.
next[5] = 3 ,以下都跟之前的一样,这种方法是最长的长度再加上一就可以了。
next[6] = 4
next[7] = 2
next[8] = 2
next[9] = 3
next[10] = 4
next[11] = 5
next[12] = 6
以上是next数组的详细解释。next数组求值 是比较麻烦的,剩下的匹配方式就很简单了。
next数组用于子串身上,根据上面的原理,我们能够推出子串a=“aab”的next数组的值分别为0,1,2.(按照我说的第二种方式算的)。
首先开始计算主串与子串的字符,设置主串用i来表示,子串用j来表示,如果ptr[i]与a[i]相等,那么i与j就都加1:
prt[1]与a[1]相等,i++,j++:
用代码实现就是
- if( j==0 || ptr[i]==a[j])
- {
- ++i;
- ++j;
- }
ptr[2]与a[2]不相等
此时ptr[2]!=a[2],那么令j = next[j],此时j=2,那么next[j] = next[2] = 1.那么此时j就等于1.这一段判断用代码解释的话就是:
- if( ptr[i]!=a[j])
- {
- j = next[j];
- }
加上上面的代码进行组合:
在对两个数组进行比对时,各自的i,j取值代码:
- while( i<ptr.length && j< a.length)
- {
- if( j==0 || ptr[i]==a[i] )
- {
- ++i;
- ++j;
- next[i] = j;
- }
- else
- {
- j = next[j];
- }
- }
此时将a[j]置于j此时所处的位置,即a[1]放到j=2处,因为在j=2时出现不匹配的情况。
此时再次计算是否匹配,可以看出来a[1]!=ptr[2],那么j = next[j],即此时j = next[1] = 0;
根据上面的代码,当j=0时,执行++i;++j;
此时就变为:
此时ptr[3] = a[1],继续向下走,下一个又不相等了,然后“aab”向后挪一位,这里不再赘述了,主要的思想已经讲明白了。到最后一直到i = 8,j=3时匹配成功,KMP算法结束。整个过程就结束了。
#include<bits/stdc++.h> using namespace std;
const int maxn = +;
int Next [maxn];
char str[maxn],mo[maxn];
void getNext()
{
int i = ,j = - , len = strlen(mo) ;
while(i<len)
{
if(j==-||mo[i] == mo[j]){Next[++i] = ++j;}
else
j = Next[j];
}
}
int kmp()
{
int i=,j=,l1=strlen(str),l2=strlen(mo);
int ans = ;
while(i<l1)
{
if(j==- || mo[j] == str[i]) i++,j++;
else j = Next[j];
if(j == l2) ans++;
}
return ans ;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s%s",mo,str);
Next[] = - ;
getNext();
printf("%d\n",kmp());
}
}
KMP算法 (字符串的匹配)的更多相关文章
- 【原创】通俗易懂的讲解KMP算法(字符串匹配算法)及代码实现
一.本文简介 本文的目的是简单明了的讲解KMP算法的思想及实现过程. 网上的文章的确有些杂乱,有的过浅,有的太深,希望本文对初学者是非常友好的. 其实KMP算法有一些改良版,这些是在理解KMP核心思想 ...
- KMP 算法 & 字符串查找算法
KMP算法 Knuth–Morris–Pratt algorithm 克努斯-莫里斯-普拉特 算法 algorithm kmp_search: input: an array of character ...
- KMP算法字符串查找子串
题目: 经典的KMP算法 分析: 和KMP算法对应的是BF算法,其中BF算法时间复杂度,最坏情况下可以达到O(n*m),而KMP算法的时间复杂度是O(n + m),所以,KMP算法效率高很多. 但是K ...
- KMP算法,查询匹配串的个数
想不到时隔两年回来重新学习KMP算法还是那么难,不过理解了大概,把例程贴上来,如果是求数量只需要加个count变量记录即可. #include"stdio.h" #include& ...
- 子字符查找KMP算法 - 子串自匹配索引表
public static int[] kmpTable(char[] seq) { int[] tbl = new int[seq.length]; tbl[0] = 1; for (int i = ...
- 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案
之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...
- 基于KMP算法的字符串模式匹配问题
基于KMP算法的字符匹配问题 反正整个清明都在纠结这玩意...差点我以为下个清明要给自己过了. 至于大体的理解,我就不再多说了(还要画图多麻烦鸭),我参考了以下两个博客,写的真的不错,我放了超链接,点 ...
- 数据结构4_java---顺序串,字符串匹配算法(BF算法,KMP算法)
1.顺序串 实现的操作有: 构造串 判断空串 返回串的长度 返回位序号为i的字符 将串的长度扩充为newCapacity 返回从begin到end-1的子串 在第i个字符之前插入字串str 删除子串 ...
- KMP算法-Java实现
目的: 为了解决字符串模式匹配 历程: 朴素模式匹配:逐次进行比较 KMP算法:利用匹配失败得到的信息,来最大限度的移动模式串,以此来减少比较次数提高性能 概念: m:是目标串长度 n:是模式串长度 ...
- [bzoj1892][bzoj2384][bzoj1461][Ceoi2011]Match/字符串的匹配_KMP_树状数组
2384: [Ceoi2011]Match 1892: Match 1461: 字符串的匹配 题目大意: 数据范围: 题解: 很巧妙的一道题呀. 需要对$KMP$算法有很深的理解才行. 首先我们需要发 ...
随机推荐
- Kubernetes v1.16 对API的更改
前段时间安装Kubernetes v1.16.2,然后从v1.14版本的拷贝yaml文件过来执行,很多都报没有相应的api,查看一下新版本的api admissionregistration.k8s. ...
- jq实现跟随鼠标点击移动的下划线效果
效果如下: 1.html代码: <div class="center-left-tap"> <a href="javascript:void (0)&q ...
- [CSP-S模拟测试]:游戏(最短路)
题目传送门(内部题35) 输入格式 第一行,两个正整数$X,Y$.第二行,三个非负整数$A,B,C$.第三行,一个正整数$N$.接下来$N$行,每行两个非负整数$x_i,y_i$. 输出格式 一行,一 ...
- Linux命令 shutdown
1. 简介 shutdown是用来让计算机处于暂停,关机,重启的指令 2.语法 shutdown [krhHPc] [时间] [警告信息] 时间的格式: hh:mm 表示多长时间以后执行 ...
- Mac-VScode
1) 安装 xcode. 打开App Store,搜索xcode,进行下载安装. 2)执行命令: xcode-select --install 3)安装VS Code https://code.vis ...
- js实现图片预览、压缩、上传
先看几个对象:Blob.ArrayBuffer.File.fileReader.formData 详细解释请参考:https://www.cnblogs.com/youhong/p/10875190. ...
- MFC DLL 导出函数的定义方式
一直在鼓捣DLL,每天的工作都是调试一个一个的DLL,往DLL里面添加自己的代码,但是对于DLL一直不太了解啊!今天一查资料,才发现自己对于DLL编写的一些基本知识也不了解.要学习,这篇文章先总结DL ...
- 20140919 进程间通信 系统栈 用户栈 多级反馈队列 windows 内存管理
1.进程间通信 共享内存(剪切板) 匿名管道只能实现父子进程间的通信(以文件系统为基础): 匿名管道是什么,有什么用,怎么用 1.创建父进程,也就是在解决方案中建立一个parent的工程 2.在par ...
- 7、服务发现&服务消费者Ribbon
公众号: java乐园 在<服务注册&服务提供者>这一篇可能学习了这么开发一个服务提供者,在生成上服务提供者通常是部署在内网上,即是服务提供者所在的服务器是与互联网完全隔离的.这篇 ...
- 与JS报错的那段时光
1.Uncaught SyntaxError: Unexpected end of input js报错: 翻译:语法错误:输入意外终止 原因:页面代码写的不规范 ╮(╯▽╰)╭ 其中的某条语句,没 ...