1.字符串
字符串
1.定义:由零个或多个字符组成的有限序列
一般记为:s=”a1a2a3……an”(n>0)
可以是空串,可以记为“”
2.字串和主串
例如:“over”是“lover”的字串
3.字符串的比较
比较每个字符的ASCII值的大小,从头比到尾
例如:“FishC”和“fishc”比较,
因为‘F’==70 ‘f’==102,’
‘f’>’F’,所以”finshc”>”FishC”
4.字符串的存储结构
与线性表相同,分为顺序存储结构与链式存储结构
顺序存储就是用数组存储
5.BF算法
朴素的模式匹配算法
核心思想:看主串中是否包含子串
两个字符串的首字符比较,若相等则同时往后一位直到结束,若匹配过程字符不等则主串向前回溯到首字符的后一位,子串回到第一位,若不匹配则主串向后移一位
效率低
6.kmp匹配
①优点:主串指针没有回溯,快速达到匹配状态。
KMP是一种高效的模式匹配算法,它牺牲了一定的空间去保存next数组,提高了我们的匹配效率。
KMP算法还能更智能的移动字符串,让字符串达到匹配效果
②公共前后缀:前面和后面一样的长度
A | B | A | B |
求这个B的公共前后缀
因为它前面有三个元素,所以前/后缀的最多元素个数为2 此时前缀为AB 后缀BA 不相等
然后前缀换成A 后缀为A 相等
公共前后缀长度为1
第三个元素的前缀后缀只能为A B ,不相等,公共前后缀为0
第一个A 的公共前后缀为-1
A | B | A | B | C |
下标 1 2 3
A | B | C |
0 1 1(公共前后缀为0,0+1=1)
③算法
找到不匹配位置前的公共前后缀,向后移动,使得前缀来到原来后缀的位置。如果有多对公共前后缀,取最长的公共前后缀。
公共前缀的长度要小于字串长度
一号位不匹配:1号位与主串下一位比较
二号位不匹配:1号位与主串不匹配字符比较
三号位不匹配:1号位与主串不匹配字符比较
四号位不匹配:2号位与主串不匹配字符比较
五号位不匹配:3号位与主串不匹配字符比较
用几号位与主串不匹配字符比较:用当前不匹配号位前的最大公共前后缀+1 ,这就是next的值
-1 0 0 1 2 3 1 1 2 3 4 5
0 1 2 3 4 5 6 7 8 9 10 11
下标从1开始
Next数组:
当该字符与主串字符不匹配之后,值对应的下标的字符要移动到原来不匹配的主串字符的位置。
Next数组求法(next数组代表最大前后缀长度+1)
当新的一位与前面最大相同的字符串后一位相等时,此时为新的最大长度的最大情况,即next[j]+1
当不相等时,新的最大长度的情况为,前前最大相同字符串长度+1
以此类推,直到相等
7.BM算法从尾部开始比较
尾部不相等时,称此时的主串字符为坏字符
子串遇到坏字符的移动规则:
子串向后移:坏字符的位置-子串中上一次出现的位置
(序号从0开始)
和子串首字符位置相对应的主串字符位置为0
若坏字符在子串中没出现,则它在子串中出现的位置为-1
尾部相等时,继续匹配,直到中间出现不匹配情况
此时所有匹配字符组成的字符串称为好后缀(好后缀可有多个字符)
子串遇到好后缀的移动规则:
子串向后移:子串好后缀的位置-子串中的上一次出现的位置(没出现则为-1)
好后缀的位置序是以最后一个字符的位置进行计算
2.BF算法实现
#include<stdio.h>
#include<stdlib.h>
typedef struct String
{
char* data;
int len;
}String;
//初始化
String* StringInit()
{
String* s=(String*)malloc(sizeof(String));
s->data=NULL;
s->len=0;
return s;
}
//赋值
void StringAssign(String* s,char* data)
{
//避免野指针
if(s->data)
{
free(s->data);
}
int len=0;
char* temp=data;
//计算赋值给它的字符数组的长度
//字符数组的最后一位是|0
while(*temp)
{
len++;
temp++;
}
//赋值给它的字符数组为空
if(len==0)
{
s->data=NULL;
s->len=0;
}
else
{
temp=data;
s->len=len;
//最后一位是|0
s->data=(char*)malloc(sizeof(char)*(len+1));
for(int i=0;i<len;i++,temp++)
{
s->data[i]=*temp;
}
}
}
//BF算法实现
void forceMatch(String* master,String* sub)
{
int i=0;
int j=0;
while(i<master->len&&j<sub->len)
{
//首字符比较,若相等则同时往后一位直到结束
if(master->data[i]==sub->data[j])
{
i++;
j++;
}
else
{
//若匹配过程字符不等则主串向前回溯到第二位,字串回到首字符
//若还不相等,主串再向后走一位
i=i-j+1;
j=0;
}
}
if(j==sub->len)
{
printf("force match succeed.\n");
}
else
{
printf("force match fail.\n");
}
}
//打印
void StringPrint(String* s)
{
for(int i=0;i<s->len;i++)
{
printf(i==0?"%c ":"->%c ",s->data[i]);
}
printf("\n");
}
int main()
{
String* s=StringInit();
String* s1=StringInit();
StringAssign(s,"HELLO");
StringAssign(s1,"fELL");
StringPrint(s);
StringPrint(s1);
forceMatch(s,s1);
return 0;
}
3.KMPP匹配实现
#include<stdio.h>
#include<stdlib.h>
typedef struct String
{
char* data;
int len;
}String;
//初始化
String* StringInit()
{
String* s=(String*)malloc(sizeof(String));
s->data=NULL;
s->len=0;
return s;
}
//赋值
void StringAssign(String* s,char* data)
{
//避免野指针
if(s->data)
{
free(s->data);
}
int len=0;
char* temp=data;
//计算长度
while(*temp)
{
len++;
temp++;
}
if(len==0)
{
s->data=NULL;
s->len=0;
}
else
{
temp=data;
s->len=len;
s->data=(char*)malloc(sizeof(char)*(len+1));
for(int i=0;i<len;i++,temp++)
{
s->data[i]=*temp;
}
}
}
//KMP算法实现
//获得next数组
int* getNext(String* s)
{
int* next=(int*)malloc(sizeof(int)*s->len);
next[0]=-1;
int i=0; //下标
int j=-1; //最大公共前后缀
next[i]=j;
while(i<s->len-1)
{
if(j==-1||s->data[i]==s->data[j])
{
++i;
++j;
next[i]=j;
}
else
{
//next[j+1]的最大值为next[j]+1
//当s->data[i]!=s->data[j]时,next[j+1]的可能的最大值为 next[next[j]]+1
//next存贮的是最大的相同字符串长度
//当新的一位与前面最大相同的字符串后一位相等时,此时为新的最大长度的最大情况
//即next[j]+1
//当不相等时,新的最大长度的情况为,前前最大相同字符串长度+1
//以此类推,直到相等
j=next[j];
}
}
return next;
}
//打印next数组
void printNext(int* next,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",next[i]);
}
printf("\n");
}
//实现匹配
void kmpMatch(String* master,String* sub,int* next)
{
int i=0;
int j=0;
while(i<master->len&&j<sub->len)
{
if(master->data[i]==sub->data[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j==sub->len)
{
printf("kmp match succeed.\n");
}
else
{
printf("kmp match fail.\n");
}
}
//打印
void StringPrint(String* s)
{
for(int i=0;i<s->len;i++)
{
printf(i==0?"%c ":"->%c ",s->data[i]);
}
printf("\n");
}
int main()
{
String* s=StringInit();
StringAssign(s,"ababaaababaa");
StringPrint(s);
String* s1=StringInit();
StringAssign(s1,"ababaa");
StringPrint(s1);
int* next=getNext(s1);
printNext(next,s1->len);
kmpMatch(s,s1,next);
return 0;
}
3.BM算法原理
//BM算法从尾部开始比较
//尾部不相等时,称此时的主串字符为坏字符
//子串遇到坏字符的移动规则:
// 子串向后移:坏字符的位置-子串中上一次出现的位置(序号从0开始)
// 和子串首字符位置相对应的主串字符位置为0
// 若坏字符在子串中没出现,则它在子串中出现的位置为-1
//尾部相等时,继续匹配,直到中间出现不匹配情况
// 此时所有匹配字符组成的字符串称为好后缀(好后缀可有多个字符)
//子串遇到好后缀的移动规则:
// 子串向后移:子串好后缀的位置-子串中的上一次出现的位置(没出现则为-1)
// 好后缀的位置序是以最后一个字符的位置进行计算