KPM算法源码

本文介绍了字符串匹配的KMP算法,通过避免回退提高效率,达到O(m+n)的时间复杂度。当处理大量重复子字符串的目标串时,KMP算法展现出显著优势。在实际运行中,若出现内存爆炸或死循环,可能是因为未正确使用fail方法导致的匹配失败。

今天突然被问起字符串匹配的算法,竟哑口无言,只剩下一些模糊的印象,回来恶补了一番。

朴素的匹配算法很简单,往前找如果有部分成功的也回退,如果目标字符串长度为m,源字符串长度为n则时间复杂度为O(m*n);

而克努斯等人发现了更快的不用回退的算法,即KMP(Knuth,Morris,Pratt)算法,在部分查找成功之后,用成功的记录确定下次匹配开始位置。时间复杂度为O(m+n);

KMP算法在目标字符串及源字符串较小的情况下运行时间和朴素匹配的时间复杂度相差不大,但一旦查找很长的字符串并且 目标字符串首尾相同的子字符串很多。

那么KMP的优势一下就体现出来了,它是跳跃式前进,比O(m*n)快了几个数量级。

	public class KMPMatch {
		/*KMP算法是朴素模式匹配算法(复杂度为O(m*n))的改进
		 * 在查找过程中不会退,运用失败函数提供重新匹配起始位置
		 * 时间复杂度为O(m+n)
		 * 是Knuth,Morris,Pratt一起提出的算法,故此命名
		 */
		
		/*这里为了性能,不用Java封装类型,一般需要匹配的字符不至于超过100,如果有需求
		*的话可以换种写法,使用构造函数来给数组指定大小
		*/
		private int [] failArray = new int [100];
		/*
		 * 如  public KPMMatch(int size){
		 *    failArray = new int [size];
		 *   }
		 * 
		 * */
		
		public int match(char [] pattern,char[] source ){
			
			
			//定义2个变量存储目标字符串长度和待查找字符串长度
			int pLength = pattern.length;
			int sLength = source.length;
			
			//定义2个变量用于表示2个字符串下标
			int pi = 0,si = 0;
			
			//在查找未结束之前
			while(pi< pLength && si <sLength){
				//如果相等,一直往前
				if(pattern[pi]==source[si]){
					++si;
					++pi;
				}//如果第一次就失败
                                 else if(0 == pi){
					++si;
				}//这里使用失败数组确定重新匹配的起始位置
                                 else{
					pi = failArray[pi-1]+1;
					
				}
			
			}
			//如果知道源字符串到结尾仍未找到,那么失败,返回-1
			if(pi < pLength)
				return -1;
			
			//否则查找成功
			return si - pLength;
		}
		
		public int fail(char [] pattern  ){
			
			
			int pLength = pattern.length;
			failArray[0] = -1;
			
		   //对所有进行计算,从第二位开始
			 for(int j = 1; j < pLength - 1;j++){
				 //开始时,pi为-1
				 int pi = failArray[j-1];
				 //如果p0p1p2 == p6p7p8 但是 p3 != p9,那么失败
		          while(pattern[j]!=pattern[pi+1] && pi > 0){
		        	  pi = failArray[j-1];
		          }		 
		          //如果成功,值加1
				 if(pattern[j]==pattern[pi+1])
					 failArray[j] = pi+1;
				 else
					 failArray[j] = -1;
				
			 }
			return 0 ;
		}


	}







public class Test {
	public static void main(String[] args) {
		
		KMPMatch kmpTest = new KMPMatch();
	        char [] p = {'c','a','b'};
		char [] s = {'c','c','a','e','f','h','b','<span style="color:#FF0000;">c','a','b'</span>,'c'};
		kmpTest.fail(p);
	    System.out.println(kmpTest.match(p, s));

		
	}
}


额,运行的时候,电脑内存突然爆炸,而且一直在运行,那就说明程序陷入了死循环,用System.out.println("hello");来逐步调试,才发现

竟然没调用fail方法,而failArray中全是0,然后match方法一直走第三个判断语句,source不向前,pattern也不向前,所以就一直在while循环里,死掉了。。。





### KPM算法的实现、原理与应用 #### 1. KPM算法的原理 KPM算法(Knuth-Morris-Pratt Algorithm)是一种高效的字符串匹配算法,其核心思想是通过预处理模式串生成一个`next`数组或`nextval`数组,从而避免在主串中回溯指针。当出现不匹配时,利用已经比较过的部分信息来确定模式串的移动位置,从而减少不必要的比较操作[^1]。 该算法的时间复杂度为O(m+n),其中m为主串长度,n为模式串长度。相比暴力匹配算法(BF算法),KPM算法通过消除主串指针的回溯问题显著提高了效率[^2]。 #### 2. KPM算法的实现 以下是KPM算法的C语言实现代码,包括`GetNextval`函数和`KmpSearch`函数: ```c #include <stdio.h> #include <string.h> // 获取nextval数组 int* GetNextval(char* p) { int pLen = strlen(p); int* next = (int*)malloc(sizeof(int) * pLen); next[0] = -1; int k = -1, j = 0; while (j < pLen - 1) { if (k == -1 || p[j] == p[k]) { ++j; ++k; if (p[j] != p[k]) { next[j] = k; } else { next[j] = next[k]; } } else { k = next[k]; } } return next; } // KMP搜索函数 int KmpSearch(char* s, char* p) { int i = 0, j = 0; int sLen = strlen(s); int pLen = strlen(p); int* next = GetNextval(p); while (i < sLen && j < pLen) { if (j == -1 || s[i] == p[j]) { i++; j++; } else { j = next[j]; } } free(next); // 释放next数组 if (j == pLen) { return i - j; // 返回匹配起始位置 } return -1; // 匹配失败 } ``` 上述代码中,`GetNextval`函数用于生成优化后的`nextval`数组,而`KmpSearch`函数则实现了基于`nextval`数组的字符串匹配逻辑[^4]。 #### 3. KPM算法的应用 KPM算法广泛应用于需要高效字符串匹配的场景,例如: - **文本编辑器**:用于快速查找特定单词或短语。 - **搜索引擎**:在大规模文本数据中进行关键词匹配。 - **生物信息学**:分析DNA序列中的特定模式。 - **网络协议解析**:从数据流中提取特定字段或标志[^3]。 ####
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值