KMP模式匹配算法优化(由next[]→nextval[])
优化方向:在匹配时减少回溯次数,以主串部分ababac和子串ababax匹配为例,显然两段末尾不同,但是计算机需要将前面都匹配一遍,直到最后,回溯时可能一次也回不到相应位置,造成循环次数增加,所以在next[]数组的基础上推导出更为合理的回溯方案nextval[]数组回溯
所以说,如果你之前用next[]数组做出来了,只需要得到在next[]数组基础上得到nextval[]数组,在接下来的匹配查找时,直接将循环体内部的next[]改为nextval[]即可,不过你需要注意的是,弄清楚你原来的next[]数组是从0开始还是从1开始的,这影响到next[]→nextval[]的转化和匹配查找的进行
一、next[]数组到nextval[]数组的推导
下面讲一讲两种nextval[]的推导,一种是next[]数组从1开始存放数值的,另一种是next[]数组从0开始存放数值的,以子串str[] = "ababax"为例:
1)第一种next[]数组是程杰《大话数据结构》中的典例,他用到的next[]数组是从1开始,这样做的好处,就是说让字符的序号与数组序号对应,对于接下来的处理,无论是将next[]处理为nextval[],还是用next[]或者nextval[]进行匹配查找都非常方便
思路 && 做法:由next[]数组推导nextval[]数组,需要用到原始字符串ababax,因为next[]是str[]推导来的,所以str[]也是从1开始的,str[1] = a, str[2] = b…str[6] = x,做法就显而易见了,令nextval[1] = 0,从第二位开始,如果第j位对应的字符与第next[j]位对应的字符相等,那么nextval[j] = nextval[next[j]],如果不相等就将next[]数组值填入nextval[],即nextval[j] = next[j]
代码实现如下:
void NextvalProces(void)
{
char str[] = " ababax";//str[0]不放待匹配字符,空格起占位作用
int i = 1, j = 0;
int n = strlen(str);
int *next = (int *)calloc(n, sizeof(int));
int *nextval = (int *)calloc(n, sizeof(int));
while (i < n - 1)
{
if (j == 0 || str[i] == str[j])
{
++i, ++j;
next[i] = j;
}
else j = next[j];//若字符不相同,则j值回溯
}
//输出next[]数组(从数组下标1开始)
for (i = 1; i < n; i++)
printf("%2d ", next[i]);
i = 1, j = 0;
while (i < n - 1)
{
if (j == 0 || str[i] == str[j])
{
++i;
++j;
if (str[i] != str[j])
nextval[i] = j;
else
nextval[i] = nextval[j];
}
else
j = nextval[j];
}
//输出nextval[]数组(从数组下标1开始)
for (i = 1; i < n ; i++)
printf("%2d ", nextval[i]);
printf("\n");
return;
}
2)第二种方法与我的上一篇博客做法相关,那里可能讲的更详细,这里给出链接:
KMP模式匹配算法(两种)
处理next[]数组从0开始存放,即子串str[]从0开始,得到的nextval[]也是从0开始,查找时与next[]数组从1开始略有不同,还是以str[] = "ababax"为例
就是利用未成熟前缀(prefix)表,将我们用maxl[]数组代替未成熟前缀(prefix)表的称呼,有兴趣可以翻阅上一篇,这里给出maxl[]数组的求法,事实上maxl[] = 未成熟prefix[] 在移位处理、首项(未成熟prefix[0])处理即让未成熟prefix[0] = -1后,得到成熟的prefix[]数组,也就是我们说的真正意义上的前缀表(即prefix[]数组),然后令prefix[i] = prefix[i]+1, i >=0可得到从0开始存放数值的next[]数组,这就使maxl[]与未成熟prefix[]与prefix[]与从0开始的next[]关系,也是我讨论的第二种情况
为了方便,这里直接给出从0开始的maxl[]数组和next[]数组,
maxl[0~5] = 0, 0, 1, 1, 2, 3
next[0~5] = 0, 1, 1, 2, 3, 4
推导nextval[]:nextval[0~5],如果maxl[i] == next[i],则nextval[i] = nextval[i-1],如果maxl[i] == next[i],nextval[i] = next[i]; i 从1开始即i >= 1;
代码实现如下:
void Nextval_Proces(void)
{
char str[] = "ababax";
int i = 1, j = 0, t = 0;
int n = strlen(str)
int *maxl = (int *)calloc(n, sizeof(int));
int *next = (int *)calloc(n, sizeof(int));
int *nextval = (int *)calloc(n, sizeof(int));
while (i < n)
{
if (str[i] == str[j])
{
len++;
maxl[i] = j;
i++;
}
else
{
if (j > 0) j = maxl[j - 1];
else
{
maxl[i] = j;
i++;
}
}
}
for (i = 0; i < N; i++)
printf("%2d ", maxl[i]);
putchar('\n');
next[0] = 0;//代替了将next[0] = -1, 再+1的繁琐过程
for (i = 1; i < n; i ++)
next[i] = maxl[i - 1] + 1;
for (i = 0; i < n; i++)
printf("%2d ", next[i]);
putchar(10);
nextval[0] = 0;
for (i = 1; i < n; i ++)
{
if (maxl[i] == next[i])
{
t = maxl[i];
nextval[i] = nextval[t - 1];
}
else
nextval[i] = next[i];
}
for (i = 0; i < n; i++)
printf("%2d ", nextval[i]);
putchar(10);
return;
}
二、KMP模式匹配查找改进后的匹配查找
与上面两种next[]数组或者说nextval[]数组对应的两种查找
1)从1开始的next[]/nextval[]的匹配查找:
void KmpSearch(void)
{
char text[] = " ababaxjcashababcjsababcsooababcoidshiababv ababaxjcababaxlkdababclkcjkaababax";
char str[] = " ababax";//text[]数组和str[]数组都是从1开始
int i = 1, j = 0;
int n = strlen(str);
int *next = (int *)calloc(n, sizeof(int));
int *nextval = (int *)calloc(n, sizeof(int));
nextval[0] = -1;
while (i < n - 1)
{
if (j == 0 || str[i] == str[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];//若字符不相同,则j值回溯
}
//输出next[]数组(从1开始)
for (i = 1; i < n; i++)
printf("%2d ", next[i]);
printf("\n");
//下面对next[]数组进行处理得到从1开始的nextval[]数组
i = 1, j = 0;
while (i < n - 1)
{
if (j == 0 || str[i] == str[j])
{
++i;
++j;
if (str[i] != str[j])
nextval[i] = j;
else
nextval[i] = nextval[j];
}
else
j = nextval[j];
}
//输出nextval[]数组(从1开始)
for (i = 1; i < n; i++)
printf("%2d ", nextval[i]);
printf("\n");
j = 1, i = 1;
while (i < m)
{
if (j == n - 1 && text[i] == str[j])
{
printf("Found at %d !\n", i - j + 1);
j = 0;
}
if (j == 0 || text[i] == str[j])
{
++i;
++j;
}
else
j = nextval[j];
}
return;
}
2)从0开始的next[]/nextval[]的匹配查找:
void KmpSearch(void)
{
char text[] = "ababaxjcash ababaxjcababaxlkdababclkcjkaababax";
char str[] = "ababax";
int i = 1, j = 0, t = 0;
int n = strlen(str);
int m = strlen(text);
int *maxl = (int *)calloc(n, sizeof(int));
int *next = (int *)calloc(n, sizeof(int));
int *nextval = (int *)calloc(n, sizeof(int));
//计算maxl[]数组
while (i < n)
{
if (str[i] == str[j])
{
j++;
maxl[i] = j;
i++;
}
else
{
if (j > 0) j = maxl[j - 1];
else
{
maxl[i] = j;
i++;
}
}
}
//输出maxl[]数组
for (i = 0; i < n; i++)
printf("%2d ", maxl[i]);
putchar(10);
//处理maxl[]数组,得到next[]数组
next[0] = 0;
for (i = 1; i < n; i ++)
next[i] = maxl[i - 1] + 1;
//输出next[]数组
for (i = 0; i < n; i++)
printf("%2d ", next[i]);
putchar(10);
//处理next[]数组,得到nextval[]数组
nextval[0] = 0;
for (i = 1; i < n; i ++)
{
if (maxl[i] == next[i])
{
t = maxl[i];
nextval[i] = nextval[t - 1];
}
else
nextval[i] = next[i];
}
//输出nextval[]数组
for (i = 0; i < n; i++)
printf("%2d ", nextval[i]);
putchar(10);
//进行kmp模式匹配查找
j = 0, i = 0;
while (i < m)
{
if (j == n - 1 && text[i] == str[j])
{
printf("Found at %d !\n", i - j + 1);
++i;
j = 0;
}
if (text[i] == str[j]) ++i, ++j;
else
{
if (j == 0) ++i;
else j = next[j - 1];
}
}
return;
}
就说那么多,如有错误,多多指正,不胜感激!
谢谢!再见