KMP算法基础分析讲解(5分钟包教包会)

[分析]

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现。其算法复杂度为O(n+m),相对于朴素的O(n^2)算法有显著的改进。

KMP的算法核心在于构造匹配数组,利用匹配数组来处理失配过程。每当失配的时候,我们检查匹配串的待匹配点之前,是否存在相同的前缀和后缀,对于某个相同的前缀和后缀,它一定同时在匹配串和原串中同时出现。此时,我们可以将待匹配串前移到前缀位置的下一位继续匹配。这就是KMP的核心。

 

现在将分为两步叙述。一是如何够构造这个匹配数组,二是如何进行匹配过程。

 

匹配数组next[]构造:建立两个指针,i和j,首先把next[0]置为0。j=0,i=1; 如果j和i匹配,则next[i]=j+1,i++,j++;

否则,j=next[j - 1],直到匹配或者j=0;此后如果匹配next[i]=j+1,否则next[i]=0;

接下来是一个构造实例:

 

初始:

j i

0 1 2 3 4 5

a b c a b y

0

 

失配:

j i

0 1 2 3 4 5

a b c a b y

0 0

 

失配:

j     i

0 1 2 3 4 5

a b c a b y

0 0 0

 

匹配:

j        i

0 1 2 3 4 5

a b c a b y

0 0 0 1

 

匹配:

   j        i

0 1 2 3 4 5

a b c a b y

0 0 0 1 2

失配过程:

   j           i

0 1 2 3 4 5

a b c a b y

0 0 0 1 2

 

失配:

j              i

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

接下来是匹配过程。

 

KMP过程:设置指针i,j分别指向原串和模式串。原串和模式串逐一匹配;

如果匹配,i++,j++。如果j==patten.length(),返回i。

如果失配,j=next[j-1]直到i指向的字符和j指向的字符匹配,如果j==0且仍然失配,则i++。

接下来是一个匹配实例:

 

i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b   y

 

j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

匹配:

i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b   y

 

j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

匹配:

   i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b   y

 

   j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

失配过程:

      i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c a b c a b y

 

j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0


依然失配,放弃,i++:

         i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b  y

 

j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

匹配:

            i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b  y

 

   j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

匹配:

            i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b  y

 

   j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0


一直匹配:

                     i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c a  b c a  b   y

 

            j

0 1 2 3 4 5

a b c a b y

0 0 0 1 2 0

 

出现失配:

                        i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b   y

 

                j

0 1 2 3 4  5

a b c a b  y

0 0 0 1 2  0

 

失配过程:

                        i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b   y

 

      j

0 1 2 3 4  5

a b c a b  y

0 0 0 1 2  0

也就是说,在j=5对应的y之前,有长度为2的前缀和后缀匹配,我们直接从这个前缀后面接着匹配就行了。

 

一直匹配:

                                    i

0 1 2 3 4 5 6 7 8 9 10 11

a b x a b c  a b c a  b   y

 

                j

0 1 2 3 4  5

a b c a b  y

0 0 0 1 2  0

 

j==6,匹配成功,返回i。

以上是KMP算法构造和实现过程。

 

 

[代码]

#include
#define DE cout << "------" << endl
#define mems(a, b) memset(a, (b), sizeof a)
using namespace std;
typedef long long ll;
typedef long double ld;

char a[10007], p[107];
int nxt[107];

int lena, lenp;

void GetNext(char p[], int len){
	int i = 1, j = 0;
	nxt[0] = 0;
	while(i < len){
		if(p[i] == p[j]){
			nxt[i] = j + 1;
			i++, j++;
		}
		else{
			while(p[i] != p[j] && j > 0) j = nxt[j - 1];
			if(p[i] == p[j]) nxt[i] = j + 1, i++, j++;
			else nxt[i] = 0, i++;
		}
	}
	for(int i = 0; i < len; i++) cout << nxt[i] << ' ';cout << endl;
}

int KMP(char a[], char p[], int lena, int lenp){
	int i = 0, j = 0;
	while(i < lena){
		if(a[i] == p[j]){
			i++, j++;
			if(j == lenp) return i;
		}
		else{
			while(a[i] != p[j] && j > 0) j = nxt[j - 1];
			if(a[i] == p[j]) i++, j++;
			else i++;
		}
	}
	return -1;
}

//abxabcabcaby
//abcaby

int main(){
	scanf("%s", a);
	scanf("%s", p);
	lena = strlen(a), lenp = strlen(p);
	GetNext(p, lenp);
	int ans = KMP(a, p, lena, lenp);
	if(ans != -1) cout << "Matched at position " << ans - lenp << endl;
	else cout << "Can't Match";
}


举例:HDU-1711


#include
#define DE cout << "------" << endl
#define mems(a, b) memset(a, (b), sizeof a)
using namespace std;
typedef long long ll;
typedef long double ld;

int a[1000007], p[10007];
int nxt[10007];

int lena, lenp;

void GetNext(int p[], int len){
	int i = 1, j = 0;
	nxt[0] = 0;
	while(i < len){
		if(p[i] == p[j]){
			nxt[i] = j + 1;
			i++, j++;
		}
		else{
			while(p[i] != p[j] && j > 0) j = nxt[j - 1];
			if(p[i] == p[j]) nxt[i] = j + 1, i++, j++;
			else nxt[i] = 0, i++;
		}
	}
	//for(int i = 0; i < len; i++) cout << nxt[i] << ' ';cout << endl;
}

int KMP(int a[], int p[], int lena, int lenp){
	int i = 0, j = 0;
	while(i < lena){
		if(a[i] == p[j]){
			i++, j++;
			if(j == lenp) return i;
		}
		else{
			while(a[i] != p[j] && j > 0) j = nxt[j - 1];
			if(a[i] == p[j]) i++, j++;
			else i++;
		}
	}
	return -1;
}

//abxabcabcaby
//abcaby

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&lena,&lenp);
		for(int i = 0; i < lena; i++) scanf("%d",&a[i]);
		for(int i = 0; i < lenp; i++) scanf("%d",&p[i]);
		GetNext(p, lenp);
		int ans = KMP(a, p, lena, lenp);
		if(ans == -1) printf("-1\n");
		else printf("%d\n",ans-lenp+1);
		//if(ans != -1) cout << "Matched at position " << ans - lenp << endl;
		//else cout << "Can't Match";
	}
}



文章出得很快,有很多细节考虑可能不充分,欢迎指出,互相学习。

你可能感兴趣的:(字符串处理)