kmp

KMP模板匹配算法——六步搞定KMP

◇◆丶佛笑我妖孽 提交于 2020-02-27 15:11:31
KMP算法——六步搞定KMP 1.什么是KMP KMP 算法是 D.E.Knuth、J,H,Morris 和 V.R.Pratt 三位大佬共同提出的,称之为 Knuth-Morria-Pratt 算法,简称 KMP 模板匹配算法。该算法相对于 Brute-Force(暴力)算法有比较大的改进,主要是消除了主串指针的回溯,提高时间效率。(空间换时间) 2.KMP与朴素模板匹配(Brute-Force) 暴力法Brute-Force 这个就是超级简单的逐一遍历比较。。。。 给定字串 S,T , 将 T 与 S 的第一个 长度与T 一样的字串比较,成功就返回匹配的第一个索引 不成功,则继续匹配 S 的下一个 长度与T 一样的字串 ……重复遍历每一个长度与 T 一样的字串,并匹配 这样的时间复杂度是O(n*m)。 KMP模板匹配算法——可以实现复杂度为O(m+n) 第一步:先了解什么是前缀后缀字串 比如: abcjkdabc,那么这个数组的最长前缀和最长后缀相同——必然是abc。 cbcbc,最长前缀和最长后缀相同——是cbc。 abcbc,最长前缀和最长后缀相同,是不存在的。 注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。 第二步,理解关于字符串匹配的规则 假设 S与T要进行匹配 ,匹配规则如图 这里aba就是最长前缀字串,然后下一次a是最长前缀字串 借别人的图示意:

比KMP更快的字符串匹配算法——BM算法

拜拜、爱过 提交于 2020-02-25 14:45:13
引言: KMP的时间复杂度是O(n+m)的 但实际上如果无法匹配成功,这个复杂度一般都是能跑满的 linux下常用的grep就用到了字符串匹配算法,但是使用了更快的BM算法 它的理论时间复杂度也是O(n+m)的,但无论能不能匹配成功,一般它都是跑不满的 也就是它实际对比的字符数量一般都是比(n+m)少很多的,所以用起来平均速度能比KMP快几倍 而最坏时间复杂度也是有保障的 原理: 考虑O(nm)的暴力对比进行字符串匹配,我们总是希望某次失配后能根据已有信息进行尽可能多的跳跃 KMP有自己的next数组来进行跳跃 BM呢?最朴素的,我们有坏字符规则 问题:我们要在长度为n的串s中找到长度为m的串t(下标均从0开始) 坏字符规则: 我们首先预处理出所有字符在t中最后一次出现的位置last-occurence 定义last[i]为字符i最后一次在t中出现的位置,可以O(m)得到last数组 我们从前往后枚举s的下标,确定某个位置后,我们从后往前枚举串t的字符,即 for i in [(m - 1) -> n]:   for j in [(m - 1) -> 0]:     ... 这样进行匹配,当j匹配到某个位置失配,那么可以直接向后移动last[] 来源: https://www.cnblogs.com/ytytzzz/p/11250277.html

POJ Oulipo KMP 模板题

我只是一个虾纸丫 提交于 2020-02-25 10:49:54
http://poj.org/problem?id=3461 Oulipo Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 41051 Accepted: 16547 Description The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e' . He was a member of the Oulipo group. A quote from the book: Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal

三步学通KMP

痴心易碎 提交于 2020-02-24 03:16:02
前言 谈到字符串模式匹配算法,莫过于最经典的KMP算法,它由D.E.Knuth,J.H.Morris和V.R.Pratt三位大牛于1977年联合发表提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法),KMP算法可以在O(n+m)的时间复杂度以内完成字符串的匹配操作,其核心思想在于:当一趟匹配过程中出现字符不匹配时,不需要回溯主串的指针,而是利用已经得到的“部分匹配”,将模式串尽可能多地向右“滑动”一段距离,然后继续比较。 说到KMP,也得需要提一下字符串的模式匹配的意义,这也是我们学习KMP及各种字符串匹配算法的意义。字符串的模式匹配是对字符串的基本操作之一,广泛应用于生物信息学、信息检索、拼写检查、语言翻译、数据压缩、网络入侵检测等领域,如何简化其复杂性一直是算法研究中的经典问题。字符串的模式匹配实质上就是寻找模式串T是否在主串S 中,及其出现的位置。由于对字符串匹配的效率要求越来越高, 所以需要不断地改良模式匹配算法,减少其时间复杂度。 说到KMP,也感觉到数学世界的美妙,我们的祖先早在2000多年前就用阴阳(0,1)虚拟的分割了这个世界,而西方近代却用0、1(阴阳)在计算机里虚拟合成了这个世界;还有数学里的那些很奇怪而有趣的问题,比如杨辉三角, Kaprekar 常数,“421陷阱”等,不知道是不是与它们最终都能转化为0、1(阴阳)有关。好了,言归正传

KMP算法

末鹿安然 提交于 2020-02-23 16:18:54
一、背景 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题。 Knuth-Morris-Pratt 算法(简称 KMP,网上有人戏称“看毛片”,我的输入法打出来的首位竟然是“烤馍片”)是解决这一问题的常用算法之一,这个算法是由高德纳(Donald Ervin Knuth)和沃恩·普拉特在 1974 年构思,同年詹姆斯·H·莫里斯也独立地设计出该算法,最终三人于 1977 年联合发表。 二、暴力匹配(朴素字符串匹配算法) 看到这样的问题,对我这个菜鸟来说,第一反应当然就是暴力匹配,代码如下: def strStr(self, haystack, needle): """ :type haystack: str :type needle: str :rtype: int """ start = 0 if needle == '': return (start) while True: if start + len(needle) > len(haystack): return (-1) else: if haystack[start:start + len(needle)] == needle: return (start) else: start += 1 暴力匹配的时间复杂度为 O(nm),其中n为 S 的长度,m为 P

KMP算法的next[]数组通俗解释

被刻印的时光 ゝ 提交于 2020-02-20 02:15:20
我们在一个母字符串中查找一个子字符串有很多方法。KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度。 当然我们可以看到这个算法针对的是子串有对称属性,如果有对称属性,那么就需要向前查找是否有可以再次匹配的内容。 在KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值越大,当然之前可能出现再匹配的机会就更大。 这个next数组的求法是KMP算法的关键,但不是很好理解,我在这里用通俗的话解释一下,看到别的地方到处是数学公式推导,看得都蛋疼,这个篇文章仅贡献给不喜欢看数学公式又想理解KMP算法的同学。 1、用一个例子来解释,下面是一个子串的next数组的值,可以看到这个子串的对称程度很高,所以next值都比较大。 位置i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 前缀next[i] 0 0 0 0 1 2 3 1 2 3 4 5 6 7 4 0 子串 a g c t a g c a g c t a g c t g 申明一下:下面说的对称不是中心对称,而是中心字符块对称,比如不是abccba,而是abcabc这种对称。 (1)逐个查找对称串。 这个很简单

KMP算法的next[]数组通俗解释

一曲冷凌霜 提交于 2020-02-20 02:10:49
KMP算法的next[]数组通俗解释 我们在一个母字符串中查找一个子字符串有很多方法。KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度。 当然我们可以看到这个算法针对的是子串有对称属性,如果有对称属性,那么就需要向前查找是否有可以再次匹配的内容。 在KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值越大,当然之前可能出现再匹配的机会就更大。 这个next数组的求法是KMP算法的关键,但不是很好理解,我在这里用通俗的话解释一下,看到别的地方到处是数学公式推导,看得都蛋疼,这个篇文章仅贡献给不喜欢看数学公式又想理解KMP算法的同学。 1、用一个例子来解释,下面是一个子串的next数组的值,可以看到这个子串的对称程度很高,所以next值都比较大。 位置i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 前缀next[i] 0 0 0 0 1 2 3 1 2 3 4 5 6 7 4 0 子串 a g c t a g c a g c t a g c t g 申明一下:下面说的对称不是中心对称,而是中心字符块对称,比如不是abccba,而是abcabc这种对称。 (1

KMP算法详解

旧街凉风 提交于 2020-02-20 01:53:48
KMP算法得核心就是对PMT得理解,下面介绍PMT: 大家只需要记住一点,PMT是什么东西。然后自己临时推这个算法也是能推出来的,完全不需要死记硬背。KMP算法的核心,是一个被称为部分匹配表(Partial Match Table)的数组。我觉得理解KMP的最大障碍就是很多人在看了很多关于KMP的文章之后,仍然搞不懂PMT中的值代表了什么意思。这里我们抛开所有的枝枝蔓蔓,先来解释一下这个数据到底是什么。对于字符串“abababca”,它的PMT如下表所示: 就像例子中所示的,如果待匹配的模式字符串有8个字符,那么PMT就会有8个值。 我先解释一下字符串的前缀和后缀。如果字符串A和B,存在A=BS,其中S是任意的非空字符串,那就称B为A的前缀。例如,”Harry”的前缀包括{”H”, ”Ha”, ”Har”, ”Harr”},我们把所有前缀组成的集合,称为字符串的前缀集合。同样可以定义后缀A=SB, 其中S是任意的非空字符串,那就称B为A的后缀,例如,”Potter”的后缀包括{”otter”, ”tter”, ”ter”, ”er”, ”r”},然后把所有后缀组成的集合,称为字符串的后缀集合。要注意的是,字符串本身并不是自己的后缀。 有了这个定义,就可以说明PMT中的值的意义了。 PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度 。例如,对于”aba”,它的前缀集合为

数据结构实验之串一:KMP简单应用

最后都变了- 提交于 2020-02-19 14:57:52
Description 给定两个字符串string1和string2,判断string2是否为string1的子串。 Input 输入包含多组数据,每组测试数据包含两行,第一行代表string1(长度小于1000000),第二行代表string2(长度小于1000000),string1和string2中保证不出现空格。 Output 对于每组输入数据,若string2是string1的子串,则输出string2在string1中的位置,若不是,输出-1。 Sample Input abc a 123456 45 abc ddd Output 1 4 -1 Hint #include <iostream> #include <stdio.h> #include <string.h> using namespace std; char str1[1000001],str2[1000001]; int nex[1000001]; void getNext() { int i=0,j=-1; nex[0]=-1; while(str2[i]!='\0') { if(j==-1||str2[i]==str2[j]) { ++i; ++j; nex[i]=j; } else { j=nex[j]; } } } void kmp() { int len1,len2; len1=strlen

数据结构--KMP

坚强是说给别人听的谎言 提交于 2020-02-19 07:13:06
KMP是一种处理字符串的方法,它的主要作用是寻找一个模板字符串p,在主字符串s中出现的位置。 在长字符串中找出一个短字符串的子串,这样的算法用暴力搜索当然是可以完成的,而且很容易考虑。但是,暴力的结果一定是极高的时间复杂度,如果长串的长度为n,短串的长度为m的话,那么暴力的时间复杂的应该是O(nm)的。 所以学会KMP还是蛮有用的,但是再学KMP之前,我们也得先用暴力的方法实现字符串的匹配,然后在对暴力算法理解优化的过程中,我们才可以更好的理解KMP算法。 1.暴力求解 就是很简单的双重循环遍历,一个一个字符的比较,若全部匹配成功则弹出,否则从长串的下一个字符处继续遍历。 const int N = 100010 , M = 10010 ; char p [ N ] , s [ M ] ; int n , m ; int main ( ) { cin >> n >> p + 1 >> m >> s + 1 ; for ( int i = 1 ; i <= n ; i ++ ) { bool flag = 1 ; for ( int j = 1 ; j <= n ; j ++ ) { if ( s [ j ] != p [ i + j - 1 ] ) { flag = 0 ; break ; } } } } 就这样的暴力方式,不难看出,每次只往后移动一个字符的遍历效率是非常低的。 2