KMP模式匹配算法优化(由next[]→nextval[])

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;
}

就说那么多,如有错误,多多指正,不胜感激!

谢谢!再见

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值