字符串匹配算法
本章重点:
1、暴力匹配(BF)算法
2、KMP算法
BF算法
百度百科:
BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。
时间复杂度为O(M*N)。
假定给出字符串"ababcabcdabcde",然后给出子串"abcd",现在查找子串是否在主串中出现,出现返回主串中的第一个匹配的下标,失败返回-1。
找到了,最后返回子串在主串中的起始位置 i-j。
/*
字符串匹配算法
str:主串
sub:子串
返回值:返回子串在主串中的起始下标,如果不存在返回-1
*/
int BF(const char* str, const char* sub)
{
assert(str != NULL && sub != NULL);
if (str == NULL || sub == NULL)
{
return -1;
}
int i = 0; // 主串下标
int j = 0; // 子串下标
int lenstr = strlen(str);
int lensub = strlen(sub);
while (i < lenstr && j < lensub)
{
if (str[i] == sub[j])
{
++i;
++j;
}
else
{
i = i - j + 1;
j = 0;
}
}
if (j == lensub)
{
return i - j;
}
return -1;
}
KMP算法
百度百科:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
区别:KMP和BF唯一不一样的地方在,主串的i并不会回退,j也不会回到0号位置。
next数组
保存子串某个位置匹配失败后,回退的位置。
KMP算法的精髓就是 next数组:也就是next[j] =k来表示,不同的j对应1个k值,这个K就是将来要移动的j要移动的位置。
K的求解:
1、规则:找到匹配成功部分的两个相等的真子串,一个以下标0开始,另一个以j-1下标结尾。
2、不管什么数据next[0]=-1; next[1]=0;
运用上面的思想,可以得出以下数组的next数组。
推导
如果p[i]==p[k],next[i+1]=k+1;
反之,继续回退去找,k=next[k]
当回退到0下标,k==-1时,next[i+1]=0;也是next[i+1]=k+1;
void GetNext(const char* p, int* next)
{
assert(p != NULL && next != NULL);
int lenp = strlen(p);
next[0] = -1;
next[1] = 0;
int i = 2; // 子串下标
int k = 0;
while (i < lenp)
{
if (k==-1 || p[i] == p[k])
{
next[i] = k + 1;
++i;
++k;
}
else
{
// p[i]!=p[k]
// 回退k
k = next[k];
}
}
}
/*
str: 主串
sub: 子串
pos: 代表从主串pos位置开始匹配
*/
int KMP(const char* str, const char* sub, int pos)
{
assert(str != NULL && sub != NULL);
int lenstr = strlen(str);
int lensub = strlen(sub);
if (lenstr == 0 || lensub == 0) return -1;
if (pos < 0 || pos >= lenstr) return -1;
// next数组
int* next = new int[lensub];
assert(next);
GetNext(sub, next);
int i = pos; //遍历主串
int j = 0; // 遍历子串
while (i < lenstr && j < lensub)
{
// j=-1,一开始就匹配不到
if (j == -1 || str[i] == sub[j])
{
++i;
++j;
}
else
{
// !=
j = next[j];
}
}
if (j == lensub)
return i - j;
return -1;
}
注意点
注意j==-1情况,
nextval数组(优化)
next数组的优化,即如何得到nextval数组
有如下串:
- 如果回退到的位置和当前字符一样,就写回退到的位置的nextval值
- 如果回退到的位置和当前字符不一样,就写当前字符原来的nextval值