字符串匹配算法之KMP

【kmp算法思想】

說起字符串查找,大家肯定能理解樸素的查法,就是以 S 每個字符爲開頭與 W 比較。O(m*n)

這種方法是基於回溯,但是這種回溯每次出現不匹配就要重頭比較,沒能很好的利用不匹配點之前已經進行的匹配比較,有很多回溯點可以直接判斷是不可能匹配成功的,因此KMP變基於對子串的提前分析,在子串的位置j出現不匹配時,直接獲得子串後移長度,減少了不必要的回溯,其效率可達到性O(m+n)。看下圖,現在字符串查找過程中出現了不匹配:

按樸素算法,這裏 W 應該右移一位然後重新匹配。但是,目前爲止,藍色色部分在兩個字符串中都是已知的。我們就不用一個個比較了,直接滑動,跳過不匹配的就好:

以上就是 KMP 算法的思想,就是針對每個不匹配點,在子串開始位置到不匹配點位置,找到最長的滑動區間。

【數學表達】

針對字符串P,定義next特徵函數如下:



其中k即爲最長的相等前綴長度,所以當S[i]!=P[j]時,我們將子串P向後滑動j-k位,此時源串指針不動,子串指針指到p[next[j]]即p[k+1],接下來只需要繼續比較S[i]和p[next[j]],因爲根據定義,前面的字符肯定相同。

【next數組的求解】

next數組的求解可以使用遞推的方式。

設已知next[j]=k+1,

則有p[0]p[1]...p[k]=p[j-1-k]p[j-k]...p[j-1],現在要求next[j+1],則:

(1)若p[0]p[1]...p[k]p[k+1]=p[j-1-k]p[j-k]...p[j-1]p[j] 即p[k+1]=p[j] 即 p[next[k]]=p[j],則next[j+1]=next[j]+1

(2)若p[next[j]]!=p[j],則next[j]肯定小於next[j]。根據KMP最長相等前綴、後綴的思路,我們對p[0]~p[k]再次找最長前綴,根據下圖所示:


因爲part1=part2=part3=part4,k=next[j],k=next[k], 

若此時p[k]=p[j],則next[j+1]=next[next[j]]+1;

否則對part1繼續上述的處理,直到k=-1.

【代碼實現】

#include <string>
#include <vector>
#include <iostream>
using namespace std;
class KMP_Algorithm
{
public:
	KMP_Algorithm(){};
	virtual ~KMP_Algorithm(){};
	static int findSubstr(string& source, string& subStr);
	static void initNext(string& str, vector<int>& Next);
	static void KMP_Algorithm_test();
};
方法實現:

#include "KMP_Algorithm.h"


int KMP_Algorithm::findSubstr(string& source, string& subStr)
{
	if (0 == source.length() || 0 == subStr.length()) return -1;
	if (source.length()<subStr.length())
	{
		cerr << "[KMP_Algorithm] source string is shorter than subStr!" << endl;
		return -1;
	}
	vector<int> Next(subStr.length(), 0);
	KMP_Algorithm::initNext(subStr, Next);
	//Next[0]=-1; Next[j]=k+1, subStr[Next[j]] will compare with the last position£¬0<=k<j-1, p(0,k)=p(j-1-k,j-1);Next[j]=0,other
	int i = 0, pos = 0;
	int SLen = source.length();
	int len = subStr.length();
	while (i<SLen && pos<len)
	{
		if (pos == -1||source[i] == subStr[pos])
		{
			i++;
			pos++;
		}
		else
		{
			pos = Next[pos];
		}
	}

	if(pos == subStr.length())
	{
		return i - subStr.length();
	}
	else return -1;
}

void KMP_Algorithm::initNext(string& str, vector<int>& Next)
{
	if (str.length() == 0) return;
	Next[0] = -1;
	int j = 0, k = -1, Len = str.length();
	while (j<Len)
	{
		if (k == -1 || str[j] == str[k])
		{
			j++;
			k++;
			if (j<Len)
				Next[j] = k;
		}
		else k = Next[k];
	}
	return;
}

void KMP_Algorithm::KMP_Algorithm_test()
{
	string s1;
	string s2;
	while (1)
	{
		cout << "[Enter source string]:";
		getline(cin, s1);
		cout << "[Enter sub string ]:";
		getline(cin, s2);
		cout << "[pos]" << KMP_Algorithm::findSubstr(s1, s2) << endl;;
	}

}
【測試】

#include "KMP_Algorithm.h"
void main()
{
	KMP_Algorithm::KMP_Algorithm_test();
	int a;
	cin >> a;

}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章