KMP算法的原理

算法思想

       每当一趟匹配过程出现字符不相等时,主串指示器不用回溯,而是利用已经得到的“部分匹配”结果,将模式串的指示器向右“滑动”尽可能远的一段距离后,再继续进行比较。

举例说明

1.

    首先,字符串“BBC ABCDAB ABCDABCDABDE”的第一个字符与搜索串“ABCDABD”的第一个字符进行比较。因为两字符不相等,所以搜索串向后移一位。

2.


    两字符不相等,继续向后移动。

3.

    重复上面操作,直到字符串中的第一个字符与搜索串中的第一个字符相等为止。

4.

扫描二维码关注公众号,回复: 949838 查看本文章


    接着,比较字符串与搜索串的下一个字符,得知相等。

5.


    重复上面操作,直到字符串与搜索串中的字符不相等为止。

6.


    这时,最容易想到的就是,将搜索串整个后移一位,再从头逐个比较。这样做,效率太差。

7.


    当空格与字符D不相等时,说明前6个字符是“ABCDAB”。KMP的算法思想是,设法利用已知信息,不要把“搜索位置”移回已经比较过的位置,继续把它向后移动。

8.

    针对搜索串,算出一张部分匹配表。

    

    字符串前缀:除了最后一个字符以外,一个字符串的全部头部组合。

    字符串后缀:除了第一个字符以外,一个字符串的全部尾部组合。

    例如:

        

    部分匹配值就是“前缀”和“后缀”的最长的共有元素的长度。

    “A”的前缀和后缀都为空集,共有元素长度为0;

    “AB”的前缀为[A],后缀为[B],共有元素长度为0;

    “ABC”的前缀为[A, AB],后缀为[BC,B],共有元素长度为0;

    “ABCD”的前缀为[A, AB,ABC],后缀为[BCD, CD, D],共有元素长度为0;

    “ABCDA”的前缀为[A,AB,ABC,ABCD],后缀为[BCDA,CDA,DA,A],共有元素长度为1;

    “ABCDAB”的前缀为[A,AB,ABC,ABCD,ABCDA],后缀为[BCDAB,CDAB,DAB,AB,B],共有元素长度为2;

    “ABCDABD”的前缀为[A,AB,ABC,ABCD,ABCDA,ABCDAB],后缀为[BCDABD,CDABD,DABD,ADB,BD,D],共有元素长度为0;

9.


    已知空格与字符D不相等时,根据部分匹配表得,最后一个匹配字符B的部分值为2。

    移动位数=已匹配字符数-对应部分匹配值。

    易得,移动位数=6-2=4.

10.


    接着继续比较。

11.


       因为空格与字符C不相等,这时已知匹配字符数为2("AB"),对应的部分匹配值为0。

        所以移动位数=2-0=2。

12.


    不相等,继续后移一位。

13.


    逐个比较,直到发现字符C与字符D不相等。

    移动位数=6-2=4

14.

    

    逐个比较,直到搜索串的最后一位,发现完全匹配,于是搜索完成。

示例程序

#include<stdio.h>
#include<string.h>
#include <stdlib.h>

void makeNext(const char P[], int next[])
{
	int q, k;
	int m = strlen(P);
	next[0] = 0;
	for (q = 1, k = 0; q < m; ++q)
	{
		while (k > 0 && P[q] != P[k])
			k = next[k - 1];
		if (P[q] == P[k]){
			k++;
		}
		next[q] = k;
	}
}

void kmp(const char T[], const char P[], int next[])
{
	int n, m;
	int i, q;
	n = strlen(T);
	m = strlen(P);
	makeNext(P, next);
	for (i = 0, q = 0; i < n; ++i){
		while (q > 0 && P[q] != T[i]){
			q = next[q - 1];
		}
		if (P[q] == T[i]){
			q++;
		}
		if (q == m){
			printf("Pattern occurs with shift:%d\n", (i - m + 1));
		}
	}
}

int main( void )
{
	int i;
	int next[20] = { 0 };
	char T[] = "BBC ABCDAB ABCDABCDABDE";
	char P[] = "ABCDABD";
	printf("%s\n", T);
	printf("%s\n", P);
	kmp(T, P, next);
	for (i = 0; i < strlen(P); ++i){
		printf("%d ", next[i]);
	}
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33408113/article/details/80163337