一、模式匹配是什么?
子串的定位运算。
模式匹配有两个字符串S和T,设S是字串,T为子串,在主串S中查找与模式T相匹配的子串,若匹配成功,确定相匹配的子串中的第一个字符在主串中出现的位置。
二、模式匹配的算法
1.BF算法(朴素模式匹配算法)
1.1 算法思路
从主串的第一个字符与模式串中的第一个字符进行比较,若相等则继续比较,若不相等则从主串中不相等的位置与模式串中的第一个字符进行比较。
1.2 如何实现
代码如下(示例):
#include <stdio.h>
#define MAXLEN 255
//顺序存储
typedef struct{
char ch[MAXLEN+1];
int length;
}SString;
int StrLength(char* ch){
int i=0;
while(ch[i]!='\0')
i++;
return i;
}
//BF算法
int Index_BF(SString S,SString T,int pos){
int i=pos,j=1;
while(i<=S.length&&j<=T.length){//找成功或S找完后仍未找到
if(S.ch[i]==T.ch[j]){//如果相同则S,T都向后挪一位。
i++;
j++;
}
else{//如果不相同则回溯到S的下一个。
j=1;
i=i-j+2;
}
}
if(j>T.length)//匹配成功
return i-T.length;
else return 0; //匹配失败
}
int main(){
SString S,T;
int index,pox;
printf("请输入字符串:");
scanf("%s",S.ch);
T.length=StrLength(T.ch);
printf("请输入要查找的子串:");
scanf("%s",T.ch);
T.length=StrLength(T.ch);
printf("请输入要开始查找的位置:");
scanf("%d",&index);
pox=Index_BF(S,T,index);
printf("返回的位置为:%d",pox);
}
1.3 时间复杂度
时间复杂度O(n*m)。
2.KMP算法
2.1 算法思路
1)相对于BF算法的改进。
每一次匹配的过程中出现字符不等时,不用回溯,而是利用已经得到的”部分匹配“的结果将模式向右滑动尽可能远的一段距离后,进行比较。
2)next数组
代码如下(示例):
void get_next(SString T,int *next)
{
int i,j;
i=1;
j=0;
next[1]=0;
while(i<T.length) //直到大于等于长度退出
{
//k=0时是第一个字符所以先给其分配数值=1,相当于字符前现只有一个值
if(j==0||T.ch[i]==T.ch[j]) //如果相同则回溯时可以一个一个与前串相比
{
++i;
++j;
next[i]=j; //相同则加一即可 可理解为在子串中回溯的位置靠后一个
}
else
j=next[j]; //如果与当前的字符不同则可以在于前一个字符进行比较,如果相同则next的数组值+1
}
}
int Index_KMP(SString S,SString T,int pos){
int i=pos,j=1;
int next[MAXLEN];
get_next(T,next);
while(i<=S.length&&j<=T.length){//找成功或S找完后仍未找到
if(j==0||S.ch[i]==T.ch[j]){//如果相同则S,T都向后挪一位。
i++;
j++;
}
else{//如果不相同则j回到next[j]的位置
j=next[j];
}
}
if(j>T.length)//匹配成功
return i-T.length;
else return 0; //匹配失败
}
完整代码如下:
# include<stdio.h>
# define MAXLEN 40
/*顺序串的存储结构*/
typedef struct {
char ch[MAXLEN];
int len;
}SString;
/*顺序串初始化*/
void StrInit(SString* s) {
s->len = 0;
}
//顺序串赋值
void StrAssign(SString* s, char* tval) {
//将字符串常量tval的值赋值给串s
int len, i = 0;
while (tval[i] != '\0')
i++;
len = i;
for (i = 0; i < len; i++)
s->ch[i] = tval[i];
s->len = len;
}
int next[MAXLEN];
//求next[]数组
/*next[j]表示模式串t的第j-1个字符前的字符串的最大前后缀*/
void GetNext(SString* t, int* next) {
int j, k;
j = 0; k = -1;
next[0] = -1; //第一个字符前无字符串,赋值-1
while (j < t->len - 1){
if (k == -1 || t->ch[j] == t->ch[k]){
j++;
k++;
next[j] = k;
}
else
k = next[k];
}
}
//KMP算法
int KMPIndex(SString* s, SString* t){
int i = 0, j = 0;
while (i < s->len && j < t->len){
if (j == -1 || s->ch[i] == t->ch[j]){
i++;
j++;
}
else
j = next[j];
}
if (j >= t->len)
return(i - t->len + 1); //返回匹配模式串的首字符下标+1
else
return(-1); //返回不匹配标志
}
int main() {
char tval_s[14] = { 'a','b','a','b','c','a','b','c','a','c','b','a','b' };
char tval_t[6] = { 'a','b','c','a','c' };
SString s, t;
StrAssign(&s, tval_s);
StrAssign(&t, tval_t);
GetNext(&t, next);
printf("%d\n", KMPIndex(&s, &t));
return 0;
}
3.3 时间复杂度
时间复杂度O(n+m)。