字符串的模式匹配
定义 在字符串中寻找子串(第一个字符)在串中的位置
相关 在串的模式匹配中,子串称为模式,主串称为目标
BF算法
运行时间为O(n*m)
1、初始时让目标T的第 0 位与模式P的第 0 位对齐;
2、顺序比对目标T与模式P中的对应字符:
(1)若 P 与 T 比对发现对应位不匹配,则本趟失配。将 P 右移一位与 T 对齐,进行下一趟比对;
(2)若 P 与 T 对应位都相等,则匹配成功,返回 T当前比较指针停留位置减去 P 的长度,即目标T 中匹配成功的位置,算法结束。
(3)若 P 与 T 比对过程中,T 后面所剩字符个数少于 P 的长度,则模式匹配失败。
模板
int Find(HStirng&T,HString&P)
{
int i, j, k; //T.n-P.n为在T中最后可比对位置
for ( i = 0; i <= T.n – P.n; i++) //逐趟比对
{
for ( k = i, j = 0; j < P.n; k++, j++ )
if ( T.ch[k] != P.ch[j] ) break; //比对不等
if ( j == P.n ) return i; //匹配成功
}
return -1; //匹配失败
}
低效的原因在于每趟重新比较时,目标 T 的检测指针要回退。
KMP 算法
若一趟匹配过程比对失配,在做下一趟匹配比对时,目标 T 的检测指针不回退,模式 P 右移(k),与 T 的检测指针对齐,再开始比对过程
字符的比较次数最多为 O(n),n 是目标 T 的长度。
图示如下:
模式右移K的确定方法
对于不同的 j(P 中的失配位置),k 的取值不同,它仅依赖于模式 P 本身前 j 个字符的构成,与目标无关。k值实际是j位前的子串的最大重复子串的长度。
可以用一个 next [ ] 失配函数来保存:当模式 P 中第 j 个字符与目标 T 中相应字符失配时,模式 P 中应当由哪个字符(设为第k+1个)与目标中刚失配的字符重新继续进行比较。next[j] = k
next 失配函数的计算
Next 失配函数从0, 1, 2, …, m-1逐项递推计算:
1、当 j = 0时,n0 = -1。设 j > 0 时 nj-1 = k:
2、当 k = -1或 j > 0且 pj-1 = pk,则 nj = k+1。
3、当 pj-1 ≠ pk 且 k ≠ -1,令 k = nk,并让3循环直到条件不满足。
4、当 pj-1 ≠pk 且 k = -1,则 nj = 0。
void getNext ( HString& P, int next[] ) {
int j = 0, k = -1;
next[0] = -1; //初始值
while ( j < P.n )
{//计算next[j]
while ( k >= 0 && P.ch[j] != P.ch[k] )
k = next[k]; //缩小前缀、后缀子串长度
j++; k++;
next[j] = k;
}
};
模板
void getNext ( HString& P, int next[] )
{
int j = 0, k = -1;
next[0] = -1; //初始值
while ( j < P.n ) //计算next[j]
{
while ( k >= 0 && P.ch[j] != P.ch[k] )
k = next[k]; //缩小前缀、后缀子串长度
j++; k++;
next[j] = k;
}
};
int fastFind ( HString& T, HString& P, int next[ ] )
{
//在目标 T 中寻找模式 P 的匹配位置。若找到,则函
//数返回 P 在 T 中开始字符下标,否则函数返回-1。
//数组next存放 P 的失配函数next[j]值
int j = 0, i = 0; //P与T的扫描指针
while ( j < P.n && i < T.n )
{ //对两串扫描
if ( j == -1 || P.ch[j] == T.ch[i] )
{ j++; i++; } //对应字符匹配,比对位置加一
else
j = next[j]; //第 j 位失配,找下一对齐位置
}
if ( j < P.n )
return -1; //j 未比完失配,匹配失败
else
return i-P.n; //匹配成功
}