朴素模式匹配算法
- 特点:匹配失败时,子串回到首位,主串回到首位加一
- 过程:从主串S的第pos个字符与子串的第一个字符开始比较,若比较成功,两串的指针加一,继续比较下一个;
若出现比较失败,子串指针回到第一个字符,主串指针回到与子串第一个字符对应字符的下一个字符,重新开始比较。 - 劣势:在二进制串中,若0或1多次重复出现,会出现多次回溯
- 函数表示:
int Index(HString S,HString T,int pos){
int i=pos,j=1;
if(T.length>S.length-pos){
return -1;
}//子串长度超过主串pos后的部分,一定不成立
while(i<S.length&&j<T.length){
if(S.ch[i-1]==T.ch[j-1]){
i++;
j++;
}
else{
i=i-j+1;
j=1;
}
if(j=T.length){
return i-T.length;
}
else{
return -1;
}
}
}
KMP算法
- 引入next数组,数组下标表示子字符串的下标,数组下标对应数字表示最长相同前缀的长度
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
子串 | A | B | C | D | A | B | F | D |
next[i] | -1 | 0 | 0 | 0 | 1 | 2 | 0 | 0 |
当i=0时,对于子串的首字符,不可能存在前缀,因此定义next【i】=-1
- next数组的函数表示
void GetNext(HString S,int next[]){
int i=0,j=-1;
next[0]=-1;
while(i<S.length){
if(j==-1||S.ch[i]==S.ch[j]){
i++;
j++;
next[i]=j;
}//注释1
else{
j=next[j];
}//注释二
}
}
以下通过画数轴更易理解
注释一:由next[i] = j
得,也就是对于位置 i 来说,区段 [0, i - 1] 的最长相同前缀分别是 [0, j - 1] 和 [i - j, i - 1],即这两区段内容相同。
注释二:next[j]
代表 [0, j - 1] 区段中最长相同前缀的长度。next【j】表示j-1段中有两小段相同,而这两小段与i-1段中后边有两段也相同,即第一段与最后一段(到i-1)相同。并且第一段的最后一个就是next【j】。这样缩短了寻找前后缀的时间。
(此图为网图)
- KMP算法
int KMP_index(HString S,HString T){
int next[T.length];
GetNext(T,next);
int i=0,j=0;
if(S.length<T.length){
return -1;
}
while(i<S.length&&j<T.length){
if(j==-1||S.ch[i]==T.ch[j]){
i++;
j++;
}
else{
j=next[j];
}
}
if(j==T.length){
return i-j;
}
return -1;
}
- 特点:主串指针不变,子串指针根据next数组进行改变
- 过程:主串和子串均从第一个开始比较,若比较成功,两串指针加一,比较下一个;
若比较失败,主串指针不变,子串指针指向next数组对应的数字(next=0,则指针指向子串第一个;next=p,则指针指向子串第p+1个) - 例子:如果子串为 abcdef,主串为abcdex,当第一轮匹配到第六个字符f和x的时候,匹配失败,由于子串f元素前的元素中没有相同的元素,并且与主串匹配,所以a与主串中的2-5号元素 即 bcde 都是不可能相匹配的,所有这几步都可以省略,直接让a和主串中的x去匹配
如果子串为abcabx,主串为abcaba,在第一轮中,前五个元素子主串分别相匹配,第六个元素位置出错,由于子串中出错的位置x前的串 abcab 的前缀和后缀都是 ab,既然第一轮的时候,已经匹配成功,那就意味着,子串中的 第一第二个元素ab一定与 主串中 第四第五个元素 ab相等,所以直接可以拿子串前缀ab后面的c开始于a进行比对
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。