问题:给定一个字符串A,要求在A中查找一个字串B。
例如,主串T=”cabcabcabcabd”,模式串P=”abcabd”
令i,j分别指向主串和模式串中字符的位置。
最基础的解法(BF算法),i,j置为0,每一次循环,主串从位置i开始,模式串从位置j开始,依次比较字符是否匹配,当T[i]==P[j]时,继续比较下一个字符,当T[i]!=P[j]时,i退回到i-j,即开始比较的位置+1,j退回到0,即下一次从模式串的起始位置重新开始比较。这样的时间复杂度是O(m*n)。KMP算法通过利用已知的匹配信息,可减少后面匹配的计算量。
KMP算法先利用一个数组next存储前缀和后缀最长的相同字符个数,next[k]存储模式串P[0…k-1]中前缀和后缀最长相同的字符个数,即next[k]=q表示P[0,q-1]==P[k-q,k-1],定义next[0]=-1,例如对于模式串P=”abcabd”,next[5]=2。采用KMP算法比较字符,当T[i]!=P[j]时,由于记录了子串P[0,j-1]中前缀和后缀相同的字符数,i可以不进行回退,j回退到next[j],因为已匹配的字串P[0,j-1]中,P[j-1-next[j],j-1]与P[0,next[j]-1]相同,而P[j-1-next[j],j-1]与主串T[i-1-next[j],i-1]是匹配的,那么P[0,next[j]-1]与T[i-1-next[j],i-1]也是匹配的,所有下一次比较只需要从P[next[j]]与T[i]开始。另外,当j==0即发生不匹配的情况时,需要++i,将next[0]置为-1以考虑该情况。
求next数组
可使用递推思想来计算:
next[0]=-1,假设next[j]=k,即P[0,k-1]==P[j-k,j-1]
(1)若P[j]==P[k],那么next[j+1]=next[j]+1
(2)若P[j]!=P[k],那么将问题转化为寻找与前缀相同的子串问题,利用KMP算法,k=next[k],重新开始比较。
代码如下
int kmpSearch(const char* pSrc, int slen, const char* patnStr, int plen)
{
if (NULL==pSrc || NULL==patnStr || slen<=0 || plen<=0 || plen>slen)
return -1;
int i,j;
int subStrIndex;
int* nextIndexArr=new int[plen];
GetNextIndex(patnStr,plen,nextIndexArr);
i=0;
j=0;
subStrIndex=-1;
while(i<slen)
{
while(i<slen && j<plen && (pSrc[i]==patnStr[j] || -1==j))
{
i++;
j++;
}
if (j==plen)
{
subStrIndex=i-plen;
break;
}
else
{
j=nextIndexArr[j];
}
}
delete[] nextIndexArr;
nextIndexArr=NULL;
return subStrIndex;
}
void GetNextIndex(const char* pStr,int len,int* nextIndexArr)
{
int i=0;
nextIndexArr[0]=-1;
int j=-1;
while(i<len-1)
{
if (-1==j ||pStr[i]==pStr[j])
{
++i;
++j;
nextIndexArr[i]=j;
}
else
{
j=nextIndexArr[j];
}
}
}
参考资料:
http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html
http://www.matrix67.com/blog/archives/115
http://www.cppblog.com/suiaiguo/archive/2009/07/16/90237.html
http://blog.chinaunix.net/uid-27767798-id-3481340.html