KMP模式匹配算法原理分析、next数组优化及java实现

朴素的模式匹配算法

 朴素的模式匹配算法通过对主串进行回溯,每次在匹配不成功时回溯到主串的i-j+2的位置

(i为主串中匹配失败的位置,j为模式串中匹配失败的位置,其中模式串字符数组从位置1开始)

KMP算法中next数组分析

KMP算法采用next数组,避免对主串的回溯,主要分析模式字符串的特征,确定在匹配失败情况下通过next数组获取应该模式串应该回溯的位置

next数组存在的意义

next数组目的在于当主串和模式串的字符匹配发生失败时,可以获取模式串回溯的位置,从新和主串进行匹配

next数组原理

在这里我们规定模式串字符数组的char[0]不存放字符,从数组的第二个位置开始,即char[1]
首先需要明确模式串中前缀和后缀的概念,对于模式串"ababaaaba",每一个字符都有其前缀和后缀
举例来讲:第五个字符a的前缀为"ab",后缀也为"ab",两部分相等,那么我们定义next[5] = 3;
第二个字符b没有前后缀,所以next[2] = 1;next[1] = 0;
在这里我们可以得到next数组如下:
next[1] = 0;next[2] = 1;next[3] = 1 ;
next[4] = 2;next[5] = 3;next[6] = 4;
next[7] = 2;next[8] = 2;next[9] = 1;
因为前缀和后缀相等,那么当匹配失败时,通过next[j]获取到模式字符串需要回溯的位置,重新和主字符串char[i]进行匹配即可,如果匹配失败通过next数组获取当前j位置模式字符串需要回溯的位置,当匹配失败时,如果匹配失败的字符没有前缀和后缀,说明模式字符串需要回溯到j=1的位置

next数组创建源码

private int[] getNext(String pStr)
	{
		int i=1,j=0,len = pStr.length();
		char[] pChar = new char[len+1];//该字符数组用来存放pStr中的每个字符,当从pChar[1]位置开始存放
		System.arraycopy(pStr.toCharArray(), 0, pChar, 1, len);//拷贝
		int[] next = new int[len+1];//定义next数组用于存放每个字符在匹配失败时,需要回溯的位置
		while(i

next数组优化

如果模式字符串为"aaaaaaab"时,其next数组为{0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 } 
当在第八个元素位置匹配失败,根据next数组,需要回溯到第七个元素
由于第八个元素和第七个元素相等,那么需要继续回溯到第六个元素,接着回溯到第五个元素.......这种回溯完全可以避免,因此在获取next数组元素值时,我们可以对齐进行一定程度的优化
在设置next[i] = j;时,我们可以判断字符数组中第i位元素和第j位元素是否相等
如果相等 将next[i] = next[j](因为如果在第j个字符匹配失败,将回溯到next[j],而此时next[i]=next[j],此次匹配肯定失败,我们可以通过这种方式来避免),如果不相等,那么next[i] = j;

优化的next数组代码如下:

	private int[] getNextVal(String pStr)
	{
		int i=1,j=0,len = pStr.length();
		char[] pChar = new char[len+1];//该字符数组用来存放pStr中的每个字符,当从pChar[1]位置开始存放
		System.arraycopy(pStr.toCharArray(), 0, pChar, 1, len);//拷贝
		int[] next = new int[len+1];//定义next数组用于存放每个字符在匹配失败时,需要回溯的位置
		while(i

KMP算法与朴素模式匹配算法的时间复杂度比较

获取next数组时间复杂度为O(M),indexKMP中while循环时间复杂度为O(N),所以真个算法的时间复杂度为O(M+N)
朴素的模式匹配时间复杂度为:O(M*N)
KMP算法只有在主串和模式串"部分匹配"时才会才会体现出他的优势,否则两者差异不大  
KMP算法应用(可借鉴和参考的思想)
首先next数组代表每个字符在匹配失败时,回溯的位置,我们可以通过next数组找到每个字符的前缀和后缀

完整代码如下:

public class KMP {
	/**
	 * 
	 *  获取模式串在主串中出现的位置
	 * @param mStr 
	 * @param pStr
	 * @param pos 如果模式串在主串中存在,返回模式串在主串中出现的位置,否则返回-1
	 * @return
	 */
	public int indexKMP(String mStr,String pStr,int pos)
	{
		int i=pos,j=0;
		int pLen = pStr.length();//模式字符串长度
		int mLen = mStr.length();//主字符串长度
		char[] mChar = mStr.toCharArray(); //主字符串字符数组
		char[] pChar = new char[pLen+1];//模式字符串字符数组从第二个元素位置开始存放
		System.arraycopy(pStr.toCharArray(), 0, pChar, 1, pLen);//拷贝
		int next[] = getNextVal(pStr);
		while(i=pLen)//主字符串中存在模式模式字符串
		{
			return i-j; //返回模式字符串在主字符串中出现的位置
		}
		
		return -1;//主字符串中不存在
	}
	/**
	 *  未经优化next数组
	 * @param pStr
	 * @return
	 */
	private int[] getNext(String pStr)
	{
		int i=1,j=0,len = pStr.length();
		char[] pChar = new char[len+1];//该字符数组用来存放pStr中的每个字符,当从pChar[1]位置开始存放
		System.arraycopy(pStr.toCharArray(), 0, pChar, 1, len);//拷贝
		int[] next = new int[len+1];//定义next数组用于存放每个字符在匹配失败时,需要回溯的位置
		while(i
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
 

你可能感兴趣的:(数据结构与算法,数据结构与算法)