1. 程式人生 > >KMP演算法(程式碼+圖解證明)

KMP演算法(程式碼+圖解證明)

        KMP演算法用於字串匹配,是相較於樸素字串匹配。所謂樸素字串匹配就是從頭到尾開始一個位置一個位置匹配,當前位置匹配失敗則會從下一個位置開始繼續匹配。

        KMP演算法則是可以跳,能往前跳多遠就往前跳多遠。我們知道演算法儘量要簡化計算、去冗餘、記憶搜尋。KMP就是利用了去冗餘的思想往前跳到某一位置繼續匹配。而中間未匹配的絲毫不會有影響,這要歸結於我們next陣列的定義。

       所謂next陣列中next[i]就是記錄如果當前位置位置不匹配,字串可以往前跳多遠進行重新匹配。所以next[i]就是計算i之前字串的最大回文長度(就是字首和字尾相等的最大長度)。中間跳躍的點都是不用匹配的點,這個可以證明。


       如圖,我們中間跳過的位置,如果存在有用的,即b'>b的長度。這種情況反證了我們b計算錯誤,也即我們next陣列沒求最長的,也就是沒求對。故而,跳過的就是沒用的。

       那麼指導跳躍的next陣列的求法如下圖所示。


       上圖演示的是next[i]這一位的計算方法,使用到了遞迴。這些說明白了,直接上程式碼。

#include<iostream>
#include<string>
using namespace std;

int * getNextArray(string ms) {
		if (ms.length() == 1) {
			int *a=new int[1];
			a[0]=-1;
			return a;
		}
		int* next = new int[ms.length()];
		next[0] = -1;
		next[1] = 0;
		int pos = 2;
		int cn = 0;
		while (pos < ms.length()) {          //next陣列從2之後大於等於0
			if (ms[pos - 1] == ms[cn]) {
				next[pos++] = ++cn;
			} else if (cn > 0) {
				cn = next[cn];
			} else {
				next[pos++] = 0;
			}
		}
		return next;
	}

//s是原串,m是模式串
 int getIndexOf(string s, string m) {
		if (s == "" || m == "" || m.length() < 1 || s.length() < m.length()) {
			return -1;
		}
		
		int si = 0;
		int mi = 0;
		int* next = getNextArray(m);
		while (si < s.length() && mi < m.length()) {
			if (s[si] == m[mi]) {
				si++;
				mi++;
			} else if (next[mi] == -1) {
				si++;   
			} else {
				mi = next[mi];
			}
		}
		return mi == m.length() ? si - mi : -1;
	}

      KMP演算法其實不難,仔細研究下就可以理解了。