您的位置:首页 > 其它

字符串匹配算法之KMP

2016-07-15 21:26 393 查看


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

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息