算法竞赛—KMP字符串匹配算法

拖延症晚期的笔者时隔N天来发第二篇blog了。其实笔者在此期间很多次都想将每个学到的新算法都写篇blog,然而笔者发现如果每篇都要很精细、将每条论述每条逻辑表达得十分清晰的话,那笔者码blog的时间的可能会过于消耗业余时间了,并且也没有想动手的动力了。
于是乎,从今往后(如果这篇之后还有续章的话),会尽量减少文字量,仅保留主要代码及必要注释。(若有幸有读者观看并对某处有疑惑,可以在评论区留言或者私信)

KMP算法——从S1字符串中快速寻找所有S2子串

例:S1字符串为ABABABC,目标子串S2为ABA,则在S1中找到起始下标分别为1与3的子串ABA。(此处将S1的首字符的下标视为1)
以下给出 C++的 KMP算法代码实现

const int M = 120000;//N大于等于字符串s1的长度 
const int N = 10000;//N大于等于字符串S2的长度

int next[N],len1,len2;//len分别为s的长度

//next[i]的含义为:在s2字符串中的1-i间的子串里,使得子串前缀与后缀相同且最长的字符个数
//例如:ABCABDD i=5时的字符是B,next[5]中1-5的子串为ABCAB
//显然当其前缀取AB,后缀也取AB时,可以使得前后缀相同,且字符个数最多为2
//因此这个例子中 next[5]=2
//在这种定义下,next[i]也等价于1-i的子串中,满足上述条件的前缀的末尾字符的下标(同见上例)

char s1[M],s2[N];//此处字符串均从下标为1开始使用

int main(){
	for(int i=2,j=0;i<=len2;i++){  //用双指针生成next数组
		//规定next[1]=0,其一是因为长度为1的字符串前缀与后缀都为同一个字符,没有意义
		//其二的原因,下文将另作说明
		while(j!=0&&s2[i]!=s2[j+1]) j=next[j];
		//j为0则说明,此时正在匹配的串没有任何字符,即正在重新开始匹配
		//s2[i]!=s2[j+1]是判断s2的第i为与i+1位相等与否(此处有个隐含条件,即s2[i-1]==s2[j]) 若不相等则退回上一个满足s2[i-1]==s2[j]的j点
		//j=next[j]即令指针j退回上一个满足s2[i-1]==s2[j]的j点
		
		//正因为j为0代表了重新匹配的开始,所以当长度为1的字符串匹配失败时,执行j=next[1]的结果就应该是0
		//这就是上文规定next[1]=0的第二个原因
		if(s2[i]==s2[j+1]) j++;//若第i位与第j+1位相等 则j++继续匹配下一个字符
		next[i]=j;//
	}

	for(int i=1,j=0;i<=len1;i++){//开始在s1串中找出所有的s2子串
		while(j!=0&&s1[i]!=s2[j+1]) j=next[j];
		if(s1[i]==s2[j+1]) j++;
		//以上两条语句与上个循环中的语句同理 不过这次双指针一个在s1一个在s2
		if(j==len2){//判断s2串如果匹配完了,说明找到了一个s2子串
			printf("%d ",j-i+1);//此处返回该子串的第一个字符的下标
		}
	}
	return 0;
}


洛谷网站KMP模板:https://www.luogu.com.cn/problem/P3375
读者可以研究一下其中border的定义来更深一步了解next数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值