1. 程式人生 > 實用技巧 >KMP演算法學習

KMP演算法學習

參考: https://www.zhihu.com/question/21923021

1.kmp

字串m,需要查詢存在m中的字串p

m長度為 len1 p長度len2

解決方法

1.暴力法

直接遍歷m,判斷p中的第一個字母是否相同,然後繼續往後判斷,如果失敗,繼續從當前字元開始,遍歷p,判斷是否相同,時間複雜度需要O(m*n)

2.Kmp法

  • KMP演算法的核心,是一個被稱為部分匹配表(Partial Match Table)的陣列

  • 字首與字尾

如果字串A=sb,則稱s為字串A的字首,b為A的字尾。要注意的是,字串本身並不是自己的字尾

PMT中的值是字串的字首集合與字尾集合的交集中最長元素的長度

  • 這個最長前後綴元素表示含義:

如果當前字串為ababab,記為S1,長度為Slen,則

字首:{a,ab,aba,abab,ababa}

字尾:{b,ba,bab,abab,babab}

交集最長元素為:abab,記為C1 ,該長度為4

該長度的意義是什麼?

參考暴力法可知:當匹配失敗的時候,我們需要從字串開頭開始比對,但是C1可以代表從0開始到長度4,即[0,4)不用匹配,因為本身他與從S1的[slen-1-4,slen-1]相等。

所以對於字串m,p來說,當匹配到m[i]、p[j]處,如果m[i]!=p[j],則p應該從p[PMT[j-1]]處開始匹配,對於p[0-PMT[j-1])字元子串來說,已經算是匹配過了。

KMP實際對比流程

  • 建立PMT表,計算出每個字元對應的PMT值,第一個字元為0
  • i,j從0開始:當m遍歷到i,p遍歷到j時(j>0),此時 m[i]==p[j] 可知從p[0,j]與m[i-j-1,i]相等,繼續向後遍歷
  • 如果m[i+1]!=p[j+1],此時我們知道對於p[0,j]==m[i-j-1,i]相等,因此可以通過pmt[j]=k,快速從p[0]處跳到p[k]處進行匹配。

next

對於PMT表我們通常使用next表來進行表示,對於PMT表來說,每次計算需要考慮PMT[j-1],所以next陣列為了計算方便,將PMT陣列整體向右移動一位,則每次計算只考慮next[j]即可。 計算方式如下:

//使用p的字首匹配p
static int[] generate(String p){
		int[] next=new int[p.length()];
		//從-1開始,字首,從0開始匹配,字尾
		int j=-1,int i=0;
		while(i<p.length()-1){
			if(j==-1 || p.charAt(i)==p.charAt(j)){
			  //相等(符合前後綴公共子串)即更新PMT值,或者第一次,直接賦值為0
				i++;
				j++;
				next[i]=j;
			}else{
			  //無法匹配,繼續尋找前一個可以匹配位置。
				j=next[j];
			}
		}
		return next;
}

KMP

    String a="helloabacs123",p="abac";
		int[] next = generate(p);
		int j=0,i=0;
		while(i<a.length() && j<p.length()){
			if(j==-1 || a.charAt(i)==p.charAt(j)){
				j++;
				i++;
			}else{
				j= next[j];
			}
		}
		if(j>=p.length()) System.out.println(i-j);
		System.out.println(-1);