字符串匹配原理及实现(C++版)
1. 字符串匹配概念
在查找操作中,我们用到很重要的概念就是字符串匹配,所谓字符串匹配就是在文本串中搜索模式串是否存在及其存在的位置。下面介绍几种字符串匹配的方法。
2. BF
2.1 原理
BF(暴力法)是一种最简单的字符串匹配算法,匹配过程如下:
文本串中的 I 和模式串中的 II 实现了匹配。
如果 I 和 II 下一个都是 A ,那么匹配长度增长,I 变成 III ,II 变成 IV 。
如果 III 的下一个是 A ,IV 的下一个是 B ,那么匹配失败,模式串向后移动一个字符,重新开始字符串匹配。
BF 的特点:
1.模式串与文本串的匹配是自左向右的进行。
2.一旦模式串与文本串失配,模式串只能向右移动一个字符。
2.2 代码实现
/*
* 暴力法:用于字符串匹配
* string t:文本串
* string p:模式串
* 返回值:返回首次匹配(完全匹配)位置(失败返回-1)
*/
int BruteForce(string t, string p){
int lenT = t.size();
int lenP = p.size();
int i, j;
for (i = 0; i <= lenT - lenP; ++i){
for (j = 0; j < lenP; ++j){
if (t[i + j] != p[j]){
break;
}
}
if (j == lenP){
return i;
}
}
return -1;
}
3. KMP
3.1 原理
可以看出,BF 中的匹配过程如下:
1.单次:多次成功,一次失败。
2.总体:多次失败,一次成功。
因为每次比对,都经历了多次成功,而经历了一次失败,然后需要模式串移动一个字符。显然,这种方法没有吸取 先前匹配成功的经验,实际上我们可以通过这种经验改进匹配过程。KMP 就是一种改进版的字符串匹配方法,匹配过程如下:
我们考虑在第一个文本串和模式串对齐方式中,I 和 II 是匹配的,那么,模式串能够从第一个对齐位置移动到下一个对齐位置的条件是 III 和 IV 是匹配的。
由此我们可以总结:
1.移动对齐方式只由文本串与模式串失配位置决定。
2.而与文本串与模式串失配位置的文本串字符无关。
3.也就是说,移动对齐方式只与模式串有关。
那么,我们完全可以根据模式串预先算出一张表,由此得到在不同的位置上失配可以移动模式串的字符距离。
在第一个对齐方式中,I 和 II 是匹配的,匹配长度是 7 个字符,那么我们可以在表中记录数字 7,即该表存储的是当前字符前面的字符串 头 和 尾 匹配的长度。
推导表格的方法我们采用递推的方法,假设已经有了第一个对齐位置的匹配,即 I 和 II 是匹配的,匹配长度是 7。
如果 I 和 II 的下一个字符都是 A,那么 I 变成 III ,II 变成 IV,即匹配长度 + 1,在表中记录数字 8。
如果 III 的下一个字符是 A ,IV 的下一个字符是 B,那么问题就不再那么简单了。
首先,细分 III 字符串,可以看到 V 和 VI 是匹配的,同理,VII 和 VIII 是匹配的。此时刚好 V 的下一个字符是 B,那么就实现了匹配, V 变成 IX,VIII 变成 X。在表格中记录数字 3。
如果 V 的下一个字符依旧不是 B,我们就可以将 V 继续细分,方法与上类似。如果细分到最后还是找不到字符 B,那么就只能将模式串移动一个字符,即只能在表中记录数字 0,表示当前字符前面的字符串 头 和 尾 匹配的长度是 0。
创建 next 表的代码如下:
/*
* 创建next表
* string p:模式串
* int next[]:next表
*/
void CreatNext(string p, int next[]){
int lenP = p.size();
int i = 0, j = -1;
next[0] = -1;
while (i < lenP -