什么是字符串匹配
- 两个字符串A和B,判断B是否是A的字串,并返回B再A中第一次出现的位置 如下图
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c7b1f7f94c7255075fc43f84979f8480.jpeg)
- 返回3
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3d220814f9231d237e9fcad38f8e0ada.jpeg)
- 没有匹配的 返回0
BF
- 我们很容易想出一种简单粗暴的方式,就是从主串开始,把字符串A和字符串B的字符逐个比较 如果出现不相同的 如第一个图片
- 把字符串B向后移动一位,从字符串A的第二位开始,把字符串A和字符串B的字符逐个比较
- 总结来说 我们可以i,j来分别记录字符再字符串A、B中的位置,当两个字符相同时则i++,j++,但不相同时则i=i-j+1,j=0
- 这种方法很好理解就是纯暴力(Brute Force)
- 下面上代码
#include<stdio.h>
#include<string.h>
int BF(char M[],char P[]){
int i,j;
for(i=0,j=0;i<strlen(M)&&j<strlen(P);){
if(M[i]==P[j]){
i++;
j++;
}
else{
i=i-j+1;
j=0;
}
}
if(j>=strlen(P))
return i-j+1;
return 0;
}
int main(){
char Main[20]="abbcefghbce";
char Pattern[20]="bce";
if(BF(Main,Pattern)!=0){
printf("匹配成功!首次出现的位置是:%d\n",BF(Main,Pattern));
}
else{
printf("匹配失败!\n");
}
return 0;
}
- 但这种算法的时间复杂度很高 如果是特殊情况 如:
字符串A:aaaaaaaaaaaaaaaaaaab
字符串B:aaab
这样来看,我们就会做很多无用功 - BF算法的最坏时间复杂度是O(mn)
KMP
- 有那么一种算法,它的目标是让模式串(我们把字符串A叫主串字符串B叫模式串)在每一轮尽量多移动几位,从而减少无谓字符串的比较
- kmp算法的精髓就是构造一个数组(前缀表)来存储最长公共前后缀(模式串的)
- 我们需要一个一个串的来找最长公共前后缀,这个最长是不包括自己本身的,具体可类比数学当中的真子集
- 具体可看下图
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e1effe6eb248f3a60b636487e37a8204.png)
- 如图右侧的棕色字体就是前缀表了,代码实现如下:
void prefix_table(char pattern[],int prefix[],int n){
prefix[0]=0;
int len=0;
int i=1;
while(i<n){
if(pattern[i]==pattern[len]){
len++;
prefix[i]=len;
i++;
}
else{
if(len>0){
len=prefix[len-1];
}
else{
prefix[i]=len;
i++;
}
}
}
}
- 一般来说我们对于这个前缀表数组所进行的操作就是把最后一位删去,在第一位的前面添上0,即将数组整体向后移动一位,prefix[0]=0;操作如下:
void move_prefix_table(int prefix[],int n){
int i;
for(i=n-1;i>0;i--){
prefix[i]=prefix[i-1];
}
prefix[0]=-1;
}
- 现在,我们所有的准备工作已经做完,接下来就可以进行KMP算法了
void kmp_search(char text[],char pattern[]){
int i=0,j=0;
int n=strlen(pattern);
int m=strlen(text);
int* prefix=(int *)malloc(n*sizeof(int));
prefix_table(pattern,prefix,n);
move_prefix_table(prefix,n);
while(i<m){
if(j==n-1&&text[i]==pattern[j]){
printf("Found pattren at %d\n",i-j+1);
j=prefix[j];
}
if(text[i]==pattern[j]){
i++;
j++;
}
else{
j=prefix[j];
if(j==-1){
i++;
j++;
}
}
}
}
完整代码
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<stdlib.h>
void prefix_table(char pattern[],int prefix[],int n){
prefix[0]=0;
int len=0;
int i=1;
while(i<n){
if(pattern[i]==pattern[len]){
len++;
prefix[i]=len;
i++;
}
else{
if(len>0){
len=prefix[len-1];
}
else{
prefix[i]=len;
i++;
}
}
}
}
void move_prefix_table(int prefix[],int n){
int i;
for(i=n-1;i>0;i--){
prefix[i]=prefix[i-1];
}
prefix[0]=-1;
}
void kmp_search(char text[],char pattern[]){
int i=0,j=0;
int n=strlen(pattern);
int m=strlen(text);
int* prefix=(int *)malloc(n*sizeof(int));
prefix_table(pattern,prefix,n);
move_prefix_table(prefix,n);
while(i<m){
if(j==n-1&&text[i]==pattern[j]){
printf("Found pattren at %d\n",i-j+1);
j=prefix[j];
}
if(text[i]==pattern[j]){
i++;
j++;
}
else{
j=prefix[j];
if(j==-1){
i++;
j++;
}
}
}
}
int main(){
char text[]="AABABCABAABABCABAACCABC";
char pattern[]="ABABCABAA";
kmp_search(text,pattern);
return 0;
}
运行结果
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d0b72e0fe1932a7218270ee51ffce06a.png)