以前在给定的字符串 s[ ] 中查找给定的子串 p[ ] 都是采用一般思路方法,即将 p 串中的每一位依次与 s 串中的每一位想比较,相同时则继续下一位的比较,不同时p又从头开始,s 上一次开始比较的地方开始,在依此进行比较,直到比较到它们其中一个的结尾。
int BFmatch(char *s,char *p)
{
int i = 0, j = 0;
int slen = strlen(s);
int plen = strlen(p);
while(i < slen && j < plen) {
if(s[i] == p[j]) {
i++;
j++;
}
else {
i = i-j+1;
j = 0;
}
}
if(j == strlen(p))
return i-j;
else
return -1;
}
这种方法是可行的也比较容易想到,但是效率不高。于是好心的人们发明了 KMP 算法,使得每次失配之后,不必总是回到 p[ ] 的开头重新比较,这样就提高了匹配查找的速度。
方法是将 p[i] 元素 之前 的前缀和后缀相同的最大长度值存入 next[i] 中,当 p[i] 与 s[ ] 中某个元素失配时,就将从p[next[i]] 开始比较。
int KMPmatch(char s[], char p[], int next[])
{
int i =0, j = 0;
int slen = strlen(s);
int plen = strlen(p);
while(i < slen && j < plen) {
if(j == -1 || s[i] == p[j]) { // j=-1表示前一次匹配中第一个字符就不相同
i++;
j++;
}
else
j = next[j];
}
if(j == plen)
return i - j;
else
return -1;
}
如何求得 next[ ]就是一个关键的问题了。
/* 如何求 next[], 采用递推的方法,假设我们已经知道了 next[j],那么如何求 next[j+1]
* 首先 next[0] = -1, 已知 p[] 中 0 -> (k-1) 与 (j-k) -> (j-1) 是匹配的,若 p[j] == p[k]
* 则 next[j+1] = next[j]+1 = k+1; 否则采取递推方式,令 k = next[k] (具体什么原因请看下面的图)
* 这样推下去,会存在两种情况:1、p[k] 一直 != p[j],这样 最后k = next[k=0] = -1;
* 从而进入 if 条件得出next[j]=0;2、或者某个时候有 p[k] == p[j] 则 也将进入 if 循环得出next[j]
* (注意 j 是增加的)
* 0 1 2 3 4 5 6 7
* p[] a b b c a b b d
* next[] -1 0 0 0 0 1 2 3
*/
void getnext(char p[], int next[])
{
int k = -1;
int j = 0;
int plen = strlen(p);
next[0] = -1;
while(j < plen-1) {
if(k == -1 || p[k] == p[j]) {
k++;
j++;
next[j] = k;
}
else
k = next[k];
}
}