/**
* 算法名称:KMP
* 算法目的:给出字符串str,和模式串ptr,要求找出str中ptr的匹配位置和个数
* 算法思想:
* 1、定义k为模式串失配位置j的前j-1长度子串的最大相等前后缀长度,(比如abcab最大前后缀长度为2)
* 2、当模式串在j位置失配时,模式串相对字符串str向后移动(子串长度-k)个位置(因为k为前j-1子串的最大前后缀长度,
* 前后缀相等,那么j失配,前j-1串的后缀和前缀已经匹配,所以不用再比较了),以减少暴力求解时的比较次数。
*/
/**
* 计算next数组
* @param ptr 模式串
* @param next next数组
* @param len 模式串长度
*/
void calc_next(const char *ptr, int *next, const int len)
{
next[0] = 0; // next[0]初始化为0,0表示不存在相同的最大前缀和最大后缀
int p = 0; // p初始化为0,p为头部比较指针
for (int i = 1; i < len; i++) // 从1开始,避免一个字符时与自身相等
{
while (p > 0 && ptr[p] != ptr[i]) p = next[p]; // 如果下一个不同,那么p就变成next[p]往前回溯,注意next[p]是小于p的,无论p取任何值。
if (ptr[p] == ptr[i]) p++; // 如果相同,p++
next[i] = p;//这个是把算的p的值(就是相同的最大前缀和最大后缀长)赋给next[i]
}
}
/**
* KMP,单模式串匹配
* @param str 目标字符串
* @param slen 目标字符串长度
* @param ptr 模式串
* @param plen 模式串长度
* @param match 记录匹配的首字符下标
* @return 匹配到的个数
*/
int KMP(const char *str, const int slen, const char *ptr, const int plen, int **match)
{
*match = new int[plen]; // 保存匹配的首字符下标
int cnt = 0;
int *next = new int[plen];
calc_next(ptr, next, plen); // 计算next数组
int p = 0;
for (int i = 0; i < slen; i++)
{
while (p > 0 && ptr[p] != str[i]) p = next[p]; // ptr与str有部分匹配
if (ptr[p] == str[i]) p++;
if (p == plen)
{
(*match)[cnt++] = i - plen + 1;
p = next[p - 1];
}
}
return cnt;
}