模式匹配:
给定主串S=“s1s2…sn”和模式串T=“t1t2…tn”,在S中寻找T的过程称为模式匹配。如果匹配成功,返回T在S中的位置;如果匹配失败,返回-1。
关于具体的算法原理和代码实现,这里推荐三个视频,视频中的图解可帮助快速理解算法原理。
懒猫老师-数据结构-(13)字符串匹配-BF算法(模式匹配)-哔哩哔哩
懒猫老师-数据结构-(14)字符串匹配-KMP算法1(模式匹配)-哔哩哔哩
懒猫老师-数据结构-(15)KMP算法2-next数组(模式匹配,字符串匹配)-哔哩哔哩
BF匹配算法描述:
1.在串S和串T中设比较的起始下标i和j;
2.循环直到S或T的所有字符均未比较完:
(1)如果S[i]==T[j],继续比较S和T的下一个字符;
(2)否则,将i和j回溯,准备下一趟比较;
3.如果T中所有字符均比较完,则匹配成功,返回匹配的起始比较下标;否则,匹配失败,返回-1;
KMP匹配算法描述:
1.在串S和串T中分别设比较的起始下标i和j
2.当S或T的所有字符均为比较完,则循环
(1)如果S[i]==T[j],继续比较S和T的下一个字符
(2)否则,将j向右滑动到next[j]位置,而i不变;(BF算法中是i移动到i-j+1,j直接回溯到0开始)
(3)如果j=-1,则将i++,j++,准备下一趟比较。
3.如果T中所有字符均比较完毕,则返回匹配的起始下标(i-j),否则返回-1(匹配失败)
难点:求next数组
1.当j=0时,next[j]=-1;(无条件赋初值,-1表示不进行字符比较,所以在算法中,当j=next[j]=-1时,需要i++)
2.就j>0时,next[j]的值为:模式串的位置从0到j-1构成的串中所出现的首尾相同的子串的最大长度
3.当无首尾相同的子串时,next[j]=0.(0表示从模式串头部开始重新比较) 。
代码如下:
#include<iostream>
#include<string.h>
using namespace std;
int BF(char S[],char T[]){
int i,j=0;
while(S[i]!='\0'&&T[j]!='\0'){
if(S[i]==T[j]){
i++;
j++;
}
else{
i=i-j+1;
j=0;
}
}
if(T[j]=='\0'){
return (i-j+1);
}
else return -1;
}
void Next(char *T,int *next){//求next数组,递归求解(只对模式串进行处理)
int j=-1;//前缀
int i=0;//后缀
next[0]=-1;//next[0]无条件赋初值为-1
while(i<strlen(T)){
if(j==-1||T[i]==T[j]){
i++;
j++;
next[i]=j;
}
else{
j=next[j];
}
}
}
int KMP(char S[],char T[]){
int i,j=0;
int next[strlen(T)];
Next(T,next) ;//根据模式串T,初始化next数组
while(i<strlen(S)&&j<strlen(T)){//不能写成S[i]!='\0'&&t[j]!='\0',因为在KMP中j的值有可能是-1,会造成数组越界的情况
if(j==-1||S[i]==T[j]){
i++;
j++;
}
else{
j=next[j];
}
}
if(j==strlen(T)){ //最后退出循环的j的值等于模式串的长度,说明匹配成功
return i-j+1;//返回匹配成功的第一个字符位置
}
else return -1;
}
int main(){
char S[100],T[100]={'\0'};//定义两个字符串S和T
cout<<"输入字符串S和T:"<<endl;
cin>>S;
cin>>T;
cout<<"1.用BF算法:主串和子串在第"<<BF(S,T)<<"个字符处首次匹配"<<endl;
cout<<"2.用KMP算法:主串和子串在第"<<KMP(S,T)<<"个字符处首次匹配"<<endl;
return 0;
}
算法分析: 设串S长度为n,串T长度为m,在匹配成功的情况下:
1.BF算法: 最好情况:不成功的匹配都发生在串T的第1个字符。 最坏情况:O(n+m) 平均复杂度:O(n×m)
2.KMP算法: 包括求next数组O(m)和算法循环O(n)两部分 O(m+n)
这个代码经过试验,可以完整运行,至于算法的详细讲解,可以去B站看上面的视频,加深了解。