字符串匹配-KMP算法
我们将介绍KMP算法进行字符串匹配,Knuth-Morris-Oratt字符串查找算法,主要用于在一个文本串(主串)查找一个模式串(子串)的出现位置。
KMP算法的核心主要在于
- 主串的 i 绝不后退
- 模式匹配串只和子串有关
算法流程
- 假设现在有一文本串S匹配到 i 位置,模式串p匹配到 j 位置:
-
如果当前字符匹配成功,则令i++,j++,然后继续匹配下一个字符;
-
如果匹配失败,通过观察失配之前匹配成功的那些字符,可以分为两种情况:
-
1.互不相等的情况,i就可以不用回退
在这种情况下,i即使回退了,也是一定会发生匹配失败的情况,无意义。 -
2.如果在失配时,失配前的字符串互有相等情况,i可能需要向前回退,之不过,我们只要证明左绿那条线和上橙那条线相等,那么i也就可以不用回退,而是让j不在回退到0,而是回退到一个合适的位置,去代替掉
代码实现
-
//KMP算法
//模式匹配串next
int* Get_Next(const char* sub)
{
assert(sub != NULL);
int len = strlen(sub);
int* next = (int*)malloc(sizeof(int)*len);
assert(next != NULL);
next[0] = -1;
next[1] = 0;
int k = next[1];
int j = 1;//通过已知推位置 j代表已知位置 j+1代表要推的未知位置
while ( j + 1 < len)
{
if (k == -1 || sub[j] == sub[k])
{
k++;
j++;
next[j] = k;
}
else
{
k = next[k];
}
}
return next;
}
//KMP算法的主串,由于i打死不回退,只会遍历一遍 整体时间复杂度O(n+m)
int KMP_search(const char* str,const char* sub,int pos)
{
assert(str != NULL && sub != NULL && pos >= 0 && pos < strlen(str));
int i = pos;
int j = 0;
int len_str = strlen(str); //保存主串有效长度
int len_sub = strlen(sub); //保存子串有效长度
int* next = Get_Next(sub);
while (i < len_str && j < len_sub)
{
if (j == -1 || str[i] == sub[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
//此时数据已经遍历完,要么找到,要么没有找到
if (j < len_sub)
{
return -1;
}
else
{
return i - j;
}
}
测试
我们输入一串字符串主串以及要匹配的子串,若匹配成功,将返回匹配成功的主串的下标位置。
算法复杂度分析
时间复杂度:
O
(
n
+
m
)
O(n+m)
O(n+m) n表示主串长度,m表示子串长度
空间复杂度:
O
(
m
)
O(m)
O(m) m表示子串(模式串)长度