首先对一个字符串,也就是长字符串的子串进行标记,标记方法如下:
例如 一个子串abcdabca,此时另j在a位置上,i在b位置上,进行标记,i和j的字符不同,则后移i,则有标记
abcdabca
0000
现在再后移i i移到a位置上,此时j仍在a位置上,所以出现了同一个字符,则令a位置为j+1=1;因此现在a的位置被标记为1;得到
abcdabca
00001
同时后移i和j,
'j i
abcdabca
j此时在b位置上,i也在b位置上,又出现了同一个字符,则令b位置为j+1 且此时j为1所以j=2,b位置为2
abcdabca
000012
同时后移i和j,
j i
abcdabca
又出现同一个字符,令c位置为j+1 j=3
abcdabca
0000123
同时后移i和j,
j i
abcdabca
此时b与a不匹配,j就为他上一个字符的值,他上一个字符为c,所以j=0,此时回到字符串的开头a
再次比较字符串进行匹配,a和a刚好匹配,所以得到
abcdabca
00001231
所以得到方法是若不匹配j为前一个字符对应的值,再进行匹配,若仍不匹配,j再跳回前一个字符对应的值,若匹配则写入j+1。
下面是KMP的整体应用
例如
abxabcabcaby
abcaby
000120
当上面指向x 下面指向c时,对应的不匹配,则子串向前看一个字符,b的临时变量为0,所以指向子串的头部,
a与x匹配吗? 当然不匹配,所以上面的指向后移至a,
直到
abxabcabcaby
*
abcaby
*
这两个*的位置又出现了不匹配,往前看一个字符
abcaby
000120
b对应的值是2,所以指向c位置,因为a 0 b 1 c 2,
所以从c又开始匹配,直到结束。KMP例子就举到这里。另外代码如下:
- #include <stdio.h>
- #include <string.h>
- int next[32] = {-999};
- /* 返回模式串T在母串S中第pos个字符的位置 */
- /* 调试小技巧 print x = value 或 set var x = value 可以改变gdb运行时变量的值 */
- int index_BM(char *S, char *T, int pos)
- {
- int i;
- int j;
- i = pos;
- j = 0;
- while ( (i < strlen(S)) && (j < strlen(T)) )
- {
- if (S[i] == T[j])
- {
- i++;
- j++;
- }
- else
- {
- i = i - j + 1;
- j = 0;
- }
- }
- /* 注意strlen(T)意味着j的取值范围为0 ~ (strlen(T) - 1) */
- if (strlen(T) == j)
- {
- return i - strlen(T);
- }
- else
- {
- return -1;
- }
- }
- void get_next(char *T, int *next)
- {
- int k = -1;
- int j = 0;
- next[j] = k;
- while (j < strlen(T))
- {
- if ( (k == -1) || (T[j] == T[k]) ) //注意等号是==,而不是=
- {
- ++k; // 注意是先加后使用
- ++j;
- next[j] = k;
- }
- else
- {
- k = next[k];
- }
- }
- }
- int index_KMP(char *S, char *T, int pos)
- {
- int i;
- int j;
- i = pos;
- j = 0;
- while ( (i < strlen(S)) && (j < strlen(T)) )
- {
- /* j = -1 表示next[0], 说明失配处在模式串T的第0个字符。所以这里特殊处理,然后令i+1和j+1。*/
- if ( (j == -1) || S[i] == T[j])
- {
- i++;
- j++;
- }
- else
- {
- j = next[j];
- }
- }
- if (strlen(T) == j)
- {
- return i - strlen(T);
- }
- else
- {
- return -1;
- }
- }
- void print_next(int next[], int n)
- {
- int i;
- for (i = 0; i < n; i++)
- {
- printf("next[%d] = %d\n", i, next[i]);
- }
- }
- int main(void)
- {
- char *s = "ababcabcacbab";
- char *t = "abcac";
- int pos = 0;
- int index;
- printf("================ BM ==============\n");
- index = index_BM(s, t, pos);
- printf("index = %d\n", index);
- printf("================ KMP ==============\n");
- get_next(t, next);
- print_next(next, strlen(t));
- index = index_KMP(s, t, pos);
- printf("index = %d\n", index);
- }
//优化过后的next 数组求法
void GetNextval(char* p, int next[])
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++j;
++k;
//较之前next数组求法,改动在下面4行
if (p[j] != p[k])
next[j] = k; //之前只有这一行
else
//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
next[j] = next[k];
}
else
{
k = next[k];
}
}
}
int KmpSearch(char* s, char* p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen)
{
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}