模式串: 想要在主串中找到某个子串
串的模式匹配: 设有主串s和子串t, 子串t的定位就是在主串s中找到一个与子串t相等的子串
通常把主串s成为目标串, 把子串t成为模式串, 模式串在子串的定位称作模式匹配
模式匹配算法
简单模式匹配
假设s=“40815150” , t=“150” 进行简单模式匹配
最终结果: i = 6 ; j = 1
基本思路:
从目标字符串的第一个字符开始和模式串的第一个字符比较:
若相等, 则继续逐个比较后续的字符;
否则, 从目标串下一个字符开始重新与模式串t的第一个字符进行比较
箱单模式匹配代码
int Index(SString S, SString T){
int k = 1;
int i = k, j = 1;
while(i<=S.length && j<T.length){
if(S.ch[i]==T.ch[i]){
++i;
++j;//继续比较后续字符串
}else{
k++;//检查后续字符串
i=k;
j=1;
}
}
if(j>T.length){
return k;
}else{
return 0;
}
}
算法的性能分析:
假设主串s的长度为n, 模式串的长度为m, 进行简单模式匹配
最好的情况下, 匹配成功, 时间复杂度为O(m)
最好的情况: 匹配失败, 时间复杂度为O(n-m+1)
最坏情况: 匹配成功, 比较(n-m+1)*m次, 时间复杂度为O(nm)
最坏情况: 匹配失败, 比较(n-m+1)*m次, 时间复杂度为O(mn)
KMP算法
假设S为’abaabaabaabaabc’, 模式串t为’abaabc’
简单模式算法匹配, 每次失配后都要一格一格往后移动, 所以指针i经常回溯
能不能让主串i不回溯, 只让j回溯
KMP算法思路
消除了主串指针的回溯, 从而使算法效率有了某种程度的提高
在每趟匹配的过程中, 当发生字符比较不相等时, 不回溯i指针, 而是利用已经得到的部分匹配结果, 将模式串向右滑动尽可能远的一段距离后, 继续进行比较
前缀: 指除最后一个字符以外, 字符串的所有头部子串
后缀: 指除第一个字符外, 字符串的所有尾部子串
公共前后缀: 前缀和后缀相等的部分
最大公共前后缀: 前缀和后缀的最长相等前后缀长度
部分匹配值: 最大公共前后缀的长度;
next数组值: 当前位置之前子串的部分匹配值+1, next[1]默认值为0
求模式串T的next数组
void get_next(SString T, int next[]){
int i=1, j=0;
next[1]=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){
++i;
++j;
next[i]=j;
}else{
j = next[j];
}
}
}
KMP算法
int Index_KMP(SString S, SString T, int next[]){
int i =1, j=1;
while(i<S.length&& j<T,length){
if(j==0||S.ch[i]==T.ch[j]){
++i;
++j;/继续比较后续字符
}else{
j=next[j];//模式串向右移动
}
if(j>T,length){//匹配成功
return i-T.length;
}else{
return 0;
}
}
}
改进的KMP算法
nextval数组的求法:
判断当前位置元素与next[i]比较:
若相同: 则nextval [i] = nextval [ next [i] ]
;
相当于nextval数组保存实际需要比较开始的位置
若不同,则nextval [ i ] = next [ i ]
改进的KMP算法的代码
void get_nextval(SString T, int next[]){
for(int j=2; j<T.length; j++){
if(T.ch[next[j]]==T.ch[j]){
nextval[j] = nextval[next[j]];//跳到实际要开始比较的地方
}else{
nextval[j] = next[i];
}
}
}
时间复杂度是(M+N)