【数据结构与算法】字符串匹配之BF&KMP算法

        BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

参考代码:

#include <iostream>
#include <string>

using namespace std;

int main()
{
	string s1,s2;//判断s2是不是s1的字串,是则返回匹配位置
	getline(cin,s1);
	getline(cin,s2);
	int len1=s1.length();
	int len2=s2.length();
	int i=0;
	int j=0;
	int num=0;
	int all=0;
	for(i=0;i<len1-len2+1;i++)
	{
		num=0;
		for(j=0;j<len2;j++)
		{
			if(s1[i+j]!=s2[j])
			{
				break;
			}
			else
			{
				num++;
			}	
		}
		if(num==len2)
		{
			all++;

		cout<<"匹配位置"<<i+1<<endl;
		}
	}
	if(all==0)
		cout<<"s2不是s1的子串"<<endl;
	return 0;
}

        KMP算法,是在BF算法的基础上,增加一个next数组,即当模式串
S与目标串T在某处失配时,不像BF算法那样蛮力回溯重新开始S和T的比较,而是参考next数组,确定比较的起点【避免没有必要的回溯操作】,next数组由模式串本身决定,与目标串没有关系。详解如下:

小例子1:【引入如何求next数组】

(目标串②)   S  2  a  x

(目标串③)   S  2  a  b  x

(目标串④)   S  2  a  b  a  x

(目标串⑤)   S  2  a  b  a  b  x

(模式串)    T  9  a  b  a  b  a  a  a  b  a

(下标)        j   0  1  2  3  4  5  6  7  8  9

(next数组) N      0  1  1  2  3  4  2  2  3

next数组求法的原理:

①在j=1的地方失配,即模式串与目标串第一个位置就不匹配,此时,目标串与模式串都下移一位重新匹配,所以,next[1]=0【所有模式串的next[1]都赋值为零】

②在j=2的地方失配,即模式串与目标串第二个位置不匹配,(失配前的位置都是匹配的),目标串位置不变,模式串更改为1,next[2]=1

在j=3的地方失配,即模式串与目标串第三个位置不匹配,(失配前的位置都是匹配的),目标串位置不变,模式串更改为1,next[3]=1

④在j=4的地方失配,即模式串与目标串第四个位置不匹配,(失配前的位置都是匹配的),目标串位置不变,模式串更改为2,next[4]=2,【这里为什么变成了2呢?引入前缀和后缀的概念】

我们知道,在失配前的位置都是匹配的,即前面的(目标串④)   S  2  a  b  a  x 是匹配的,理论上如果将next[4]=1,即目标串匹配位置不变,模式串重新从a(第一个位置)开始匹配,造成错检验

例: S a b a b a c

        T a b a c

失配位置      x

如果next[4]=1

       S a b a b a c

失配位置      x

                 T a b a c

所以,漏检,而当
next[4]=2

        S a b a b a c

失配位置      x

              T a b a c

这里next数组值是多少由前缀后缀重合数目决定

第四位置失配时,模式串前面已匹配的是a  b  a ,从【左向右】为前缀 从【右向左再从左到右】为后缀,zuo a==you a,所以next=2(1+1);

同样,第五位置失配时,模式串面已匹配的是a  b  a  b,前缀a b等于后缀  a  b,随意next的值为3(2+1

小例子2:【改进next数组】

(模式串)   T  6  a  a  a  a  a  c

(下标)       j   0  1  2  3  4  5  6 

(next数组)N  X  0  1  2  3  3  5

(new)       N  X  0  0  0  0  0  5

按照例子1介绍的next数组求解方法,我们得到这里的next数组值。

但我们发现一个特别的地方,在j=4的位置失配,原始的next计算方式得到next值为3

        S a a a w

失配位置      x

           T a a a a a c

但模式串的形式决定,j=4的位置失配时,j=3,也绝对会失配。。。。。因为都是相等的,造成不必要的检索过程

引入newnext数组的计算方法,模式串失配位置的字符如果和模式串前面的字符有相等的,则此位置的next值,被赋予了前面的next值

(模式串)   T  6  a  a  a  a  a  c

(下标)       j   0  1  2  3  4  5  6 

(next数组)N  X  0  1  2  3  3  5

失配位置j=1               x

(new)       N  X  0  

此时,失配位置j=1和前面有重复的,所以,它的next数值被赋值为了0.以此类推

参考代码:

#include <iostream>
#include <string>
using namespace std;

void get_next(string T,int *next)
{
	int j=0;
	int i=1;
	next[1]=0;
	while(i<(T[0]-48))//T[0]存的是长度
	{
		if(j==0 || T[i]==T[j])//i记录后缀的位置,j记住前缀的位置
		{
			i++;
			j++;
			if(T[i]!=T[j])
			{
				next[i]=j;
			}
			else
			{
				next[i]=next[j];
			}
		}
		else
		{
			//j回溯
			j=next[j];
		}
	}
	//因为前缀是固定的,后缀是相对的
}
//返回子串T在主串S第pos个字符之后的位置
//若不存在则返回0
int index_kmp(string S,string T,int pos)
{
	int i=pos;
	int j=0;
	int next[255];
	get_next(T,next);//由子串T确定next数组
	while(i<=S[0]-48&&j<=T[0]-48)
	{
		if(j==0||S[i]==T[j])
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];
		}
	}
	if(j>T[0]-48)//最后一个位置匹配成功,找到子串在主串中的位置
	{
		return i-(T[0]-48);
	}
	else
	{
		return 0;
	}
}

int main()
{
	string SS="3abc";
	string TT="2bc";
	cout<<index_kmp(SS,TT,0)<<endl;
	return 0;
}

参考链接:http://billhoo.blog.51cto.com/2337751/411486

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/haoxiaodao/article/details/47679647
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞