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