【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;
}