字符串处理、KMP模板:【模板】

KMP算法模板(next函数和kmp)

能写出一下内容首先主要感谢一下:July算法从头到尾彻底理解KMP,讲得是究极详细,文底还有视频版讲解。
还有河海大学ACM字符串专题讲解,两个小姐姐一真一假让我笑哭,文章中我主要用了这个视频里的模板,很好用,自己手模上三五遍也就懂得差不多了。
还有实验室小伙伴们,无私地和我分享资源、和我谈论帮助我理解······
还有一个播放量挺高的:kmp字符串匹配算法,有两节。

KMP板子代码如下:(一些有助于理解的内容写在了注释中,有不足希望大家不吝指教)

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
char t[2000];
char p[2000];
int nxt[2000];
//建立公共前后缀表, /*****next[j] = k 代表p[j] 之前的模式串子串中,有长度为k 的相同前缀和后缀*****/
void GetNext(char p[], int nxt[]){	// 
	int i, j;
	int m = strlen(p);
	nxt[0] = 0;
	for(j = 1, i = 0; j < m; j++){	//前缀 i,从0开始 后缀j,从1开始 
		while(i > 0 && p[j] != p[i])
			i = nxt[i-1];
		if(p[j] == p[i]){			//前后匹配时,next就加一 
			i++;
		}
		nxt[j] = i;	//①p[j] != p[i] && i == 0,赋值0
					//②p[j] == p[i] && i == 0,i已经加一(i在原匹配串上增加) 
	}				//③p[j] == p[i] && i != 0,回溯过后,给值为第一次匹配的后一位 
} 
/*可以将nxt值后移一位,首位赋值为 -1 */
/*void MoveNext(int nxt[]){
	int m = strlen(p);
	for(int i = m-1; i > 0; i++){
		nxt[i] = nxt[i-1];
	}
	nxt[0] = -1;
}*/
int kmp(char t[], char p[], int nxt[]){
	unsigned long n, m;
	int i, q;
	n = strlen(t);
	m = strlen(p);
	GetNext(p, nxt);	 
	for(i = 0, q = 0; i < n; i++){	// i时text串下标, q时p串下标
		while(q > 0 && p[q] != t[i])
			q = nxt[q-1];			// q-1前边的时公共前后缀,已经无需再比较,和p[nxt[q-1]]比较就行了 
		if(p[q] == t[i]){
			if(p[q] == t[i]){		// 匹配不断后移q和i(i在for循环中已经移动) 
				q++;
			}
			if(q == m)
				return i - m + 1;	// i为最后位置,m为长度,那起始位置就是 i-m+1 
		} 
	}
	return -1;		//根本没找到 
}
/* 直接返回循环串个数版本 
int kmp(char t[], char p[], int nxt[]){
	unsigned long n, m;
	int i, q, count = 0;
	n = strlen(t);
	m = strlen(p);
	GetNext(p, nxt);
	for(i = 0, q = 0; i < n; ++i){
		while(q > 0 && p[q] != t[i])
			q = nxt[q-1]; 
		if(p[q] == t[i]){
			q++;
		}
		if(q == m){
			count ++;
		}
	}
	return count;
}
*/
int main(){
	scanf("%s",t);
	scanf("%s",p);
	int num = kmp(t, p, nxt);
	printf("%d", num);
	return 0;
}
测试:

main函数改成这样:

int main(){
	scanf("%s",t);
	scanf("%s",p);
	int n = strlen(p);
	int num = kmp(t, p, nxt);
	for(int i = 0; i < n; i++)
		printf("%d", nxt[i]);
	printf("\n%d", num);
	return 0;
}
结果:(正确得到的nxt数组)

ccacmeracmee
acmeracme
000001234
2

心得:

我的next数组求的是包括当前字符的子串有长度位nxt[i]的相同前缀和后缀。额外写了两个函数,①:MoveNext是将nxt值后移一位,也就是常见的首位为-1,nxt[j] = k 代表p[j] 之前的模式串子串中,有长度为k 的相同前缀和后缀。
②:kmp函数是直接返回循环串个数的版本,例题:hdoj1686[http://acm.hdu.edu.cn/showproblem.php?pid=1686]。直接在上一个kmp函数的版本上修改就行了。一般情况大家只需要用没有被注释的内容就行了。大部分讲解都在注释中了,建议对nxt求法还是云里雾里的朋友们看一下我贴的那两个视频。
学kmp我也是经历了一天半的自闭循环,终于明白一二之后,一下午就AC了四个同类型题。前一天半是真的难受,这是开始学算法以来第一次有这种经历:

while(1){
	看;看不懂;出去逛一圈;看;看不懂;心烦;自闭;自闭;
}

但是在拨开云雾见光明时,收获总是很大的,如果大家也有这样的想法的话,可以这样想,千万不要放弃,先努力地理解代码,然后会用板子,因为我相信随着经验的增长,终有一天会顿悟的。小菜鸡的我也会在这条路上坚持不懈的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值