- 浏览: 897402 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
小宇宙_WZY:
膜拜一下大神,解决了我一个大问题,非常感谢 orz
【解惑】深入jar包:从jar包中读取资源文件 -
JKL852qaz:
感谢,遇到相同的问题!
【解惑】深入jar包:从jar包中读取资源文件 -
lgh1992314:
为什么java中调用final方法是用invokevirtua ...
【解惑】Java动态绑定机制的内幕 -
鲁曼1991:
说的都有道理,protected只能被同一级包的类所调用
【解惑】真正理解了protected的作用范围 -
鲁曼1991:
...
【总结】String in Java
模式匹配: 在字符串S中,子串P的定位操作通常称做串的模式匹配。说白了,就是在一个字符串中寻找子串。在Suffix Trie和PAT tree中我们已经讨论过匹配子串的方法了。这里我们讨论一种线性匹配算法来寻找子串。
例: 我们要在S="ababcabcacbab"中查找子串P="abcac"。下图左侧是一种很普通的模式匹配算法
这种普通的模式匹配算法很简单,但时间复杂度是O(n*m)。其中n=S.length,m=T.length. 代价很高。难道真的要像第三趟到第四趟那样:好不容易匹配到S中的第7个位置,但由于不相同,则有回溯到第4个位置重新开始。
其实,每一次匹配过后,主串S中被匹配过的位置其实不用再匹配了。也就是上一趟匹配结果对下一趟有指导作用。 我们用一个证明来说明这一点(假如主串S=s1 s2 .... sn ,模式串P=p1 p2 ... pm )。
证明:当一趟匹配完成之后,我们发现si !=pj 。那么至少说明了模式串p1... p(j-1)是匹配成功的。也就是得到了: p1 p2 ... p(j-1) = s(i-j+1) s(i-j+2) ..... s(i-1) 。 比如第三趟 s7!=p5 => s2...s6=p1...p4 = "abca".
如果像上图左侧算法那样,从第三趟i=7回溯到第四趟i=4是有必要的话,那么也说第四趟可能完全匹配到了模式串P。好,我们现在就假设si != sj之后,i 回溯主串(i-j+1)的下一个位置上可以匹配成功模式串P,那么我们可以得到 p1 p2 ... p(j-1) =s(i-j+2) s(i-j+3) ... s(i)。
合并下标蓝色的两个式子,我们可以得到:
s(i-j+1) s(i-j+2) ..... s(i-1)= s(i-j+2) s(i-j+3) ... s(i)
也就是: s(i-j+1)= s(i-j+2)= s(i-j+3)= .... =s(i-1)=s(i)
我靠,主串S中全部的字符都一样,这种情况下才必须每一次都回到主串的下一个位置上重新开始。而只要有字符不同,就完全没有这个必要了。好了,基于这个证明成果,我们开始介绍KMP算法。
KMP 模式匹配 —— D.E.Knuth V.R.Pratt和J.H.Morris同时发现的。因此人们称它为克努特-莫里斯-莫拉特操作(简称KMP算法)。KMP的优势在于当每一趟匹配结果中出现了不等情况时,主串并不需要回溯位置i (上面已经证明),而只要 回溯模式串P即可 。也就是说,只需要在主串S的位置i 处重新与模式串的位置k进行比较(如上图右侧过程)。 那么这个 重新 需要定位的模式串k位置有什么要求呢,或者说我们怎么确定这个k呢。我们再用一个小小的证明来揭示( 还是假如主串S=s1 s2 .... sn ,模式串P=p1 p2 ... pm ):
证明:当一趟匹配完成之后,我们发现si !=pj 。此时首先可以肯定的是k< j,因为模式串j 必须回溯。
如果 s1 与 pk 有重新比较的必要,那么模式串P前k-1个字符必须满足下列关系式:
p1 p2 ... p(k-1)=s(i-k+1) s(i-k+2) ... s(i-1)
此外,由于经过额一趟的匹配之后,已经可以得到“部分匹配”结果,主串S中i 位置的前k个字符一定等于模式串P中j 位置上的前k个字符:
p(j-k+1) p(j-k+2) ... p(j-1) = s(i-k+1) s(i-k+2) ..... s(i-1) 。
合并两个蓝色的式子:
p1 p2 ... p(k-1) = p(j-k+1) p(j-k+2) ... p(j-1)
我们发现了,位置k的值取决于模式串P自己必须满足上面这个红色的式子。
失效函数: 当在模式串P的第j 个位置上发生匹配不成功时,需要将模式串回溯到位置 k处的这样一个f(j)=k 的函数,就叫做失效函数。其中j 和k 的值必须满足p1 p2 ... p(k-1) = p(j-k+1) p(j-k+2) ... p(j-1)。 也就是说 pj 的前k-1个字符必须等于pk 的前k-1 个字符。因此,失效函数f(j)的定义如下:
0 当j=1时
f(j) = Max{k|1<k<j 且 p1...p(k-1)=p(j-k+1)...p(j-1) }
1 不满足上面的情况
比如模式串P=“abcac”的每一个位置的失效函数如下:
j 1 2 3 4 5
P a b c a c
k=f(j) 0 1 1 1 2
失效函数的算法
假如f(j)=k,则表明 p1...p(k-1) = p(j-k+1)...p(j-1)。这说明 pj 的前k-1个字符必须等于pk 的前k-1 个字符。也就是说p1...pk一定是p1...pj的一个后缀子串。因此我们可以把模式串P与自身做KMP算法的匹配来求解这个K值。算法如下:
(1) 若 pk=pj, 则p1...p(k-1)pk= p(j-k+1)...p(j-1) pj 。 表明f(j+1)=k+1
(2) 若 pk!=pj , 则可以把求f(j+1)看成以P为主串和匹配串的模式匹配问题。即 pk!=pj 则比较pj与p(f(k)),如果pj==p(f(k)),则f(j+1)=f(k)+1。否则继续比较下去直到f(k)=0为止。
失效函数算法的运行时间是o(m).
KMP算法Java源代码
package net.hr.algorithm.string; public class KMP { private int[] next=null; private char[] mainAry=null; private char[] patternAry=null; public KMP(String main,String pattern){ this.mainAry=new char[main.length()+1]; this.patternAry=new char[pattern.length()+1]; System.arraycopy(main.toCharArray(),0,mainAry,1,main.length()); System.arraycopy(pattern.toCharArray(),0,patternAry,1,pattern.length()); this.next=new int[pattern.length()+1]; } /** * KMP匹配 * @param pos 从主串起始位置开始 */ public int match(int pos){ if(pos>mainAry.length) throw new IndexOutOfBoundsException(); failFunction(); int i=pos,j=1; while(i<mainAry.length&&j<patternAry.length){ if(j==0||mainAry[i]==patternAry[j]){ ++i; ++j; }else{ j=next[j]; } } if(j>=patternAry.length) return i-patternAry.length+1; else return 0; } /** * 匹配串失效函数 */ private void failFunction(){ int i=1,j=0; next[1]=0; while(i<patternAry.length-1){ if(j==0||patternAry[i]==patternAry[j]){ next[++i]=++j; }else{ j=next[j]; } } } /** * 测试 * @param args */ public static void main(String[] args) { KMP kmp=new KMP("acabaabaabcacaabc","abaabcac"); System.out.println(kmp.match(1)); } }
KMP算法效率
对于长度为n的文本串和长度为m的模式串进行模式匹配的运行时间为O(n+m) . 很显然,因为文本串在KMP算法中并不需要回溯,因此与模式串的比较次数为O(n)。但模式串要建立失效函数,所付出的代价是O(m)。因此总体的时间复杂度是O(m+n)。实际中,m要远小于n,因此近似可以认为KMP效率为O(n)。
但是KMP算法有种最坏的情况,当模式串P="aaaaa"时,即每一个字符都一样的时候。则失效函数为:
j 1 2 3 4 5
P a a a a a
f(j) 0 1 2 3 4
此时如果主串中的s[i]!=p[j]的时候,根据模式串P回溯j=f(j)的原则。s[i]需要从模式串P的最后一个字符一步一步回溯到第一个字符,每次都要比较一遍。这时的时间复杂度为O(m)。那么对于n个字符的S串而言,最差的时间复杂度就是O(n*m)了,退化成了蛮力匹配。
KMP和后缀树都可以用来匹配子串。因此我们这里与后缀树做一个比较,虽然后缀树在查找的过程中只需要大概O(m)的时间复杂度。对长度n的文本串建立后缀树最好的算法需要O(n)时间复杂度,因此后缀树大致也需要O(n+m) 。
发表评论
-
★经典问题—链表中的环问题
2010-06-29 14:43 4455转载:http://www.cppblog.com ... -
★经典问题—元素选择问题
2010-05-24 10:53 3356元素选择问题 : 给定线性序集中n个元素和一个整数k( ... -
【串和序列处理 8】最长平台问题
2010-04-28 16:41 37141、经典最长平台算法 已知一个已经从小到大 ... -
【排序结构7】 基数排序
2010-04-20 16:17 4583《桶排序 》中我们能够看到,数据值的范围越大,可能需要桶的个 ... -
【排序结构6】 桶排序
2010-04-19 20:28 23074从《基于比较的排序结构总结 》中我们知道:全依赖“比较”操作的 ... -
【排序结构5】 基于比较的内部排序总结
2010-04-18 16:15 6760★ 基于“比较”操作的内部排序性能大PK ... -
【排序结构4】 归并排序
2010-04-17 16:29 3321归并排序 O(N*logN) 是另一种效率很高的排序方法。&q ... -
【排序结构3】 选择排序
2010-04-14 21:10 3667(1) 简单选择排序 O(N^2) 一趟简单选择排序 ... -
【排序结构2】交换排序
2010-04-14 11:04 26381、起泡排序 O(N^2) ... -
【排序结构1】插入排序
2010-04-13 17:11 30321、基本概念介绍 (1) 如果待排序列中有两个 ... -
【串和序列处理 7】LIS 最长递增子序列
2010-03-25 16:37 5843LIS: 给定一个字符串序列S={x0,x1,x2,.. ... -
【串和序列处理 6】LCS 最长公共子序列
2010-03-23 17:38 8843LCS:又称 最长公共子序列。 其中子序列(subse ... -
★经典问题—欧几里得求最大公约数
2010-03-20 19:21 3388问题:快速求取正整数a,b的最大公约数? ... -
【串和序列处理 4】Suffix Trie 子串匹配结构
2010-03-20 15:15 9140Suffix Trie : 又称后 ... -
【串和序列处理 3】Trie Tree 串集合查找
2010-03-18 13:40 17707Trie 树, 又称字典树,单词查找树。它来源于retr ... -
【串和序列处理 2】字符串编辑距离算法
2010-03-15 08:45 11115我们来看一个实际应用。现代搜索技术的发展很多以提供优质、高效的 ... -
【串和序列处理 1】PAT Tree 子串匹配结构
2010-03-14 19:10 11320Patricia Tree 简称PAT tree ... -
【查找结构6】动态查找树比较
2010-03-12 13:32 11306我们这个专题介绍的动态查找树主要有: 二叉查找树(BST),平 ... -
【查找结构4】红黑树 [RBT]
2010-03-10 10:58 10612大部分转载:http://yanglongylj.blog.1 ... -
【查找结构5】多路查找树/B~树/B+树
2010-03-09 11:56 18227在前面专题中讲的BST、A ...
相关推荐
在本实验报告中,主题是“串的操作与KMP模式匹配算法”,这涉及到计算机科学中的字符串处理和算法设计。实验的目的是让学生掌握基本的串操作以及实现著名的Knuth-Morris-Pratt(KMP)模式匹配算法。串在计算机科学中指...
在实际应用中,KMP算法广泛应用于文本搜索、生物信息学序列比对等需要高效字符串匹配的领域。然而,KMP算法在理解上存在一定的难度,特别是next数组的计算方法与原理。因此,在教学过程中,通过实例讲解和适当的理论...
KMP(Knuth-Morris-Pratt)字符串模式匹配算法是一种高效的字符串搜索算法,由D.M. Knuth、V. Morris和J.H. Pratt在1970年提出。该算法避免了简单匹配...因此,KMP算法在处理包含重复子串的模式时,性能优势尤为明显。
《KMP算法详解——高效字符串匹配的秘密》 ...总的来说,KMP算法以其高效性和广泛的应用性,成为字符串匹配领域不可或缺的一部分。通过理解和掌握KMP算法,不仅可以提升编程技能,还能为解决实际问题提供有力的工具。
KMP(Knuth-Morris-Pratt)算法是一种在文本中高效查找子串出现位置的字符串搜索算法。它由D.E.Knuth、V.R.Morris和J.Pratt三位学者于1970年提出。KMP算法的核心是构建一个部分匹配表(也称为失败函数),用于在主串...
总之,KMP算法是字符串匹配问题的一种经典解决方案,通过部分匹配表优化了匹配过程,具有较高的效率和实用性。在学习和理解KMP算法的过程中,不仅可以掌握其基本原理,还可以深入探讨如何优化字符串处理算法,提升...
本文将详细解析三种常见的字符串匹配算法:Brute Force(暴力搜索)、KMP(Knuth-Morris-Pratt)以及BM(Boyer-Moore)。这些算法在文本处理、数据搜索、生物信息学等多个领域有着广泛的应用。 首先,让我们来了解...
标题“查找主串中出现的子串的首位置.zip”和描述“抓穿中查找出现的子串的首先位置(kmp/sunday算法实现)”涉及到了字符串匹配算法,特别是KMP(Knuth-Morris-Pratt)算法和Sunday算法。这两种算法都是为了高效地...
**Knuth-Morris-Pratt(KMP)**算法是一种高效的字符串匹配算法,它可以避免在匹配失败后进行不必要的回溯。KMP算法的核心思想是利用模式字符串本身的特性,预先计算出一个“部分匹配表”,以此来优化匹配过程。 1. *...
基于c语言实现。作为一个IBM的研究人员,请写一个程序找出给定的DNA片段之间的相同之处,使得对个体的调查相关联。 一个DNA碱基序列是指把在...给出一个DNA碱基序列的集合,确定在所有序列中都出现的最长的碱基序列
- 实现主函数,使用循环结构执行KMP算法,包括比较字符、移动指针和处理失配。 - 可以添加一些辅助函数,如打印失配表、打印匹配结果等,以提高代码可读性。 4. **性能分析**:KMP算法的时间复杂度为O(n),其中n...
在计算机科学中,字符串(串)是一系列字符的集合,它们可以用来表示文本信息。...总之,理解和掌握C语言中的字符串处理和模式匹配算法是编程中的一项基础技能,对于编写涉及文本处理、搜索和分析的程序具有重要意义。
KMP算法,全称Knuth-Morris-Pratt字符串搜索算法,是一种线性时间复杂度的字符串匹配算法。它的主要思想是在发生不匹配时,能知道部分已经匹配的字符序列的后缀和模式串的前缀存在重复,因此可以利用这些信息避免...
KMP算法是一种高效且实用的字符串模式匹配算法,尤其适用于需要频繁进行字符串搜索的场景。通过对前缀表的有效构建和利用,KMP算法能够在保证线性时间复杂度的同时,提供准确的匹配结果,是现代编程中不可或缺的一...
Hirschberg算法则是在线性时间复杂度内解决最长公共子序列问题的一种方法,也可以应用于字符串匹配。它通过分治策略将原问题分解为两个子问题,并通过动态规划来解决。Hirschberg算法的一个关键优势是避免了过多的...
C语言中的KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,它主要用于在主串(也称为文本串)中查找子串(也称为模式串)的位置。KMP算法避免了对已匹配部分的重复比较,提高了搜索效率。在计算机科学中...
KMP算法是一种避免了重复比较的高效字符串匹配算法。它通过构造失配表,记录模式串中每个字符前缀和后缀的最长公共长度,从而在遇到不匹配时快速跳过已比较过的部分。KMP算法的时间复杂度为O(n+m),其中n是文本串...
KMP算法,全称为Knuth-Morris-Pratt算法,是一种高效的字符串匹配算法,由D.M. Knuth、V. Morris和J.H. Pratt在1970年提出。相较于简单的暴力匹配算法,KMP算法避免了不必要的字符回溯,大大提高了匹配效率,其时间...