KMP算法java版,next数组求法简单易懂

本文深入讲解了KMP算法的核心原理及其实现过程,重点介绍了如何计算next数组,并提供了一个Java版本的实现示例。

KMP的算法核心是:当匹配失败时返回失配位前的最长公共前后缀


详细看     github

而KMP最重要的是求next数组,而next数组求法最重要的是继承

由于网上简单的kmp算法一般都是C版的,所以我这里写了个java版的demo

public class KMP {
	 public static void main(String[] args){  
		 String S = "BBC ABCDAB ABCDABCDABDE";//主串
		 String T = "ABCDAB";//匹配串
	        KMP kmp=new KMP(S, T);  //计算next数组	          
	        kmp.printNext();  //输出next数组
	        int theLoc=kmp.getIndexOfStr();  //获得匹配位置	          
	        System.out.println();  
	        System.out.println("匹配位置在:"+theLoc);  	          
	    }  	      
	    private int[] next=null;  //next数组
	    private String S=null;   //主串
	    private String T=null;  //子串   
	   
	    public KMP(String S,String T){  
	        this.S=S;  
	        this.T=T;  
	        next=next();
	    }  
	    /** 
	     * 计算next 数组的值。 
	     * next数组的值为对称位的下标(从0开始),没有对称的设置为-1
	     * next求值核心是:继承
	     * */  	    
	     public int[] next(){
	    	 if(T==null || T.length()==0){
	    		 return null;//先检测是否空或者为""
	    	 }else{
	    		 int next[] = new int[T.length()];
	    		 next[0]=-1;//第一位一定为-1
	    		 for(int i=1;i<T.length();i++){//从第二位开始起
	    			 int j = next[i-1];//j的值与当当前位前一位的next有关
	    			 char T_j = T.charAt(j+1);
	    			 char T_m = T.charAt(i);
	    			/*
	    			 * 由于当前位的next继承前一位的next,当前一位的next不为-1时,比较当前位与前一位next+1的值,相等则加1
	    			 * 不相等则在找出更小的对称性
	    			 */
	    			 while(T_j!=T_m&&j>=0){
	    				j=next[j];
	    				T_j = T.charAt(j+1);
	    			 }
	    			 if(T_m == T_j){
	    				 next[i] = j+1;
	    			 }
	    			 else{
	    				 next[i]=-1;
	    			 }
	    		 }
	    		 return next;
	    	 }  	
	     }
	     public void printNext(){  
	         if(next!=null){  
	             System.out.println("next array的值:");  
	             for(int tmp:next){  
	                 System.out.print(tmp+"   ");  
	             }  
	         }  
	     }  
	       /*
	        * 获取匹配位置
	        */
	       public int getIndexOfStr(){
		        int i = 0;
		        int j =0;
		        char sChar;
		        char tChar;
		        /*
		         * 当主串到达尾部时,判断子串是否到达尾部,到达则求出子串在主串的位置,否为为-1
		         * 位置:子串第一个字符在主串的位置=主串匹配位-子串长度
		         */
		        while(i<S.length() && j<T.length()){
		        	sChar = S.charAt(i);
		        	tChar = T.charAt(j);
		        	if(sChar == tChar){//匹配时都递增
		        		i++;
		        		j++;
		        	}else{
		        		//不匹配时判断子串位置,若为第一位主串加一位,不是的话按next跳转
		        		if(j==0){
		        			i++;
		        		}else{
		        		j=next[j-1]+1;//跳转到前一位的next加1
		        		}
		        	}     	
		        }
		        return (j==T.length())?(i-j):-1;
	       }		
}


<think>嗯,用户让我解释KMP算法的原理,而且要简单易懂。首先,我得回想一下KMP的基本概念。KMP算法是用于字符串匹配的,相比于暴力匹配,它更高效。那它的核心思想是什么呢?应该是利用已经部分匹配的信息来跳过不必要的比较,从而减少时间复杂度。 接下来,我需要确定用户可能的背景。他可能在学习算法或者遇到字符串匹配的问题,想了解KMP如何工作。用户可能对暴力匹配的低效有体会,比如在长文本中查找子串时时间过长。因此,我需要对比暴力方法,突出KMP的优势。 KMP的关键在于部分匹配表,也就是前缀函数或者next数组。这个表记录了模式串中每个位置的最长公共前后缀长度。这样,当匹配失败时,可以根据这个表移动模式串,而不是每次只移动一位。例如,在模式串"ABABC"中,如果某个位置不匹配,利用next数组可以知道应该移动几位,避免重复比较。 我需要用简单例子来说明这个过程。比如,主串是"ABABABCABAABABABCABAA",模式串是"ABABC"。当匹配到某个字符失败时,如何根据next数组调整位置。可能需要分步骤解释匹配过程,以及如何计算next数组。 不过用户可能对如何构建next数组不太清楚,所以需要详细解释这部分。构建next数组的过程其实也是模式串自我匹配的过程。比如,对于每个位置i,找到最大的k,使得前k个字符和后k个字符相同。这样,当匹配失败时,模式串可以直接跳到k的位置继续比较,而不用回退主串的指针。 可能用户还会关心时间复杂度,KMP是O(n+m),而暴力是O(n*m)。这点也需要提到,但重点还是原理的解释。需要用通俗的语言,避免太多数学公式,而是通过例子和步骤说明。 最后,给出一个C++的实现示例,展示如何构建next数组和使用KMP进行匹配。代码需要注释清晰,结构正确。确保用户能看懂每个步骤的作用,比如如何初始化next数组,如何更新指针i和j。 现在,检查是否有遗漏点。比如,为什么要用最长公共前后缀?这样移动模式串能保证不错过可能的匹配,同时跳过不必要的比较。这部分可能需要进一步解释,但用户要求简单易懂,所以可能不需要太深入数学证明。 总结,回答应该包括:KMP解决的问题,暴力法的不足,部分匹配表的作用,构建next数组的方法,以及匹配过程的步骤。最后用代码示例巩固理解。</think><< KMP算法是一种高效的字符串匹配算法,其核心思想是通过预处理模式串构建「部分匹配表」(也称next数组),避免匹配失败时的重复比较。以下是关键原理解释: 1. **暴力匹配的缺陷**: 传统暴力匹配每次失配时,主串指针回退并从头比较,时间复杂度O(m*n) 2. **部分匹配表(next数组)**: 记录模式串每个位置的最长公共前后缀长度,用于确定失配时模式串的滑动距离 3. **匹配过程优化**: 当字符失配时,根据next数组值跳过已匹配的前缀部分,主串指针无需回退 示例代码: ```cpp #include <vector> using namespace std; vector<int> buildNext(const string& pattern) { vector<int> next(pattern.size(), 0); for (int i = 1, j = 0; i < pattern.size();) { if (pattern[i] == pattern[j]) { next[i++] = ++j; } else { j ? j = next[j-1] : i++; } } return next; } int kmpSearch(const string& text, const string& pattern) { vector<int> next = buildNext(pattern); int i = 0, j = 0; while (i < text.size()) { if (text[i] == pattern[j]) { i++; j++; if (j == pattern.size()) return i - j; // 找到匹配 } else { j ? j = next[j-1] : i++; } } return -1; // 未找到 } ``` 运行示例: ```cpp string text = "ABABABCABAABABABCABAA"; string pattern = "ABABC"; cout << kmpSearch(text, pattern); // 输出匹配位置2 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值