昨晚一直在啃KMP,总算是对这个久仰的算法有了大概的了解。
KMP(字符串匹配)算法对于初学者来说肯定是一块难啃的骨头(这个比喻真无聊=_=)
为了理解这个精妙的算法,我参考了以下博客:
http://www.matrix67.com/blog/archives/115 matrix67大牛的KMP讲解,在网上看KMP的基本都看过吧。。。
http://chaoswork.com/blog/2011/06/14/kmp%E7%AE%97%E6%B3%95%E5%B0%8F%E7%BB%93/ 这篇文章条理很清晰
还有新买的《大话数据结构》等等,图文并茂,他们总结得很好,很适合初学者。
下面是我自己根据书上的代码修改而来的包含测试用例的KMP算法:
#include <iostream>//KMP TEST
#include <string>
using namespace std;
int Next[1001];//next 数组
void getNext(const string &str)
{
int len=str.length();
int i=1,j=0;
Next[i]=j;
while(i<len)
{
if ( j==0 || str[i-1]==str[j-1] )//这里很需要注意,因为字符串第一个下标是0不是1,需要减1,很多算法书抄来抄去而没有指明这一点(下同)
{
i++;j++;
Next[i]=j;
}
else
j=Next[j];//回溯到上一个匹配的位置
}
}
int index_KMP(const string &S,const string &T,int pos)//返回子串T在主串S中第Pos个字符之后的位置
{
int i=pos,j=0;
int slen=S.length() , tlen=T.length();
getNext(T);//调用上面的getNext函数
while( i<slen && j<tlen )
{
if( j==0 || S[i-1]==T[j-1] ) { i++; j++;}//根据Next数组实行回溯或者进行下一次比较
else j=Next[j];
}
if(j>=tlen ) return i-tlen+1;//这里是返回第四个,因为字符串从0开始计,所以要加1
else return -1;//表示不存在
}
int main()
{
string str1("qweabcaabxasd"),str2("abcaabx");
int index;
index=index_KMP(str1,str2,0);//获取下标
if(index==-1) cout<<"Can not find the word"<<endl;
else cout<<"Yeap! The word display in the index of "<<index<<endl;
cout<<endl;
return 0;
}
可以说,KMP算法的关键是求出Next数组,那么,为什么需要这个数组呢。
我的理解:为了在子串在与主串比较中不相等时, j回溯到哪个位置,或者说子串向右移动多少位。
而Next数组是根据子串的结构的自我匹配而生成的,说白了就是先给自己标记一下,哪些地方是可以跳过比较的,这就是Next数组存在的意义,
也就是KMP比朴素字符串匹配的优越之处。
然而我还不太清晰的是,为什么Next[ Next[j] ]就是应该回溯的位置。