kuangbin带你飞 KMP & 扩展KMP & Manacher总结(一)

终于把kmp给搞完了,这两天会把所有的题以及一些重要的知识点总结一下。

这一章先讲一下kmp的部分题和重要点,kmp算法讲解可以看这几篇文章:

(原创)详解KMP算法

从头到尾彻底理解KMP

KMP算法最浅显理解——一看就明白

kmp算法主要的作用有三点:单个字符的匹配,循环节以及前后缀问题。

先抛出kmp的代码实现:

#include <iostream>
using namespace std;
const int maxn=1e6+7;
string s,p;
int nxt[maxn];
void getnext(string s){
	int len=s.size();
	int k=-1,j=0;
	nxt[0]=-1;
	while(j<len){
		if(k==-1||s[j]==s[k])
			nxt[++j]=++k;
		else k=nxt[k];
	}
}
int kmp(string s,string p){
	int len1=s.size();
	int len2=p.size();
	getnext(p);
	int i=0,j=0;
	while(i<len1){
		if(j==-1||s[i]==p[j])
			i++,j++;
		else j=nxt[j];
		if(j==len2)
			return i-len2+1; 
	}
	return -1;
}
int main(int argc, char** argv) {
	while(cin>>s>>p){
		printf("%d\n",kmp(s,p));
	}
	return 0;
}

①单个字符的匹配: 这个算是kmp的模板题,不过问法通常有:求出现次数(可重叠和不可重叠),开始位置,判断是否匹配。

下面给出代码:(来自网上)


int KMP_Count(char s[], char p[]){
	kmp_pre();
	cnt = 0;
	int i,j;
	i = j = 0;
	int  n = strlen(s);
	int m = strlen(p);
	while(i < n){
	    while(j != -1 && s[i] != p[j])	j = nextt[j];		
	    ++i;  ++j;
	    if(j >= m){		
		cnt++;              //记录p在s中出现的个数             
		j = nextt[j];	//p在s中可以覆盖、重叠,例如s=abababa p=aba,最后cnt=3
		//j = 0;	//p在s中不可以覆盖、重叠,例如s=abababa p=aba,最后cnt=2
               //return 1;         //返回存在
           //return i-m+1;      //返回p首次在s中出现的下标
	}
    }
    return cnt;                 //返回出现次数
    //return 0;                 //返回不存在
   // return -1;                   //p首次在s中出现的下标为-1,表示s中没有p
}

②循环节(next数组的妙用之一):首先要知道next数组的定义:next[i]表示s[0..i-1]的最长公共前后缀长度。

首先我们需要知道一些公式:

如果字符串循环,那么满足两个条件:i % (i-next[i]) == 0&&next[i] != 0

循环长度= i - next[i]

循环次数= i / ( i -next[i])

下面是证明——来自网上

我们先假设到达位置 i-1 的时候,字符串循环了(到i-1完毕),那么如果到第i个字符的时候,失配了,
根据next数组的求法,我们是不是得回溯?
然而回溯的话,由于字符串是循环的了(这个是假定的),
next[i] 是不是指向上一个循环节的后面一个字符呢??
是的,上一个循环节的末尾是 next[i]-1 ,然后现在循环节的末尾是 i-1 ,然么循环节的长度是多少呢?
所以,我们有 (i - 1) - ( next[i] - 1 ) = i - next[i]  就是循环节的长度(假设循环成立的条件下)
现在我们已经假设了 0————i-1 循环了,那么我们就一共有i 个字符了,
如果有 i % ( i - next[i] ) == 0,总的字符数刚好是循环节的倍数,那么说明这个循环是成立的。
注意还有一点,如果 next[i] == 0,即使符合上述等式,这也不是循环的,举个反例
0   1    2   3   4   5
a    b   c   a   b   d
-1   0   0   0   1   2 
下标为1,2,3的next值均为0,那么 i%(i-next【i】)=i%i==0,但是这个并不是循环。
解释完毕,然后再来看下,为什么求出来的循环节长度是最小的呢?
因为next数组失配的时候,总是回溯到最近的循环节,所以i-next【i】就是最小的循环节长度
    为什么求出来的循环次数是最多的呢?
    循环节长度是最小的了,那么循环次数肯定是最多的了。

③前后缀问题(next数组妙用之二):因为next表示s[0..i-1]的最长公共前后缀长度。我们可以通过一个while循环,不断迭代,直到nextt[i]为0,可以求出字符串所有的前后缀相等的长度。

例如,ababcababababcabab,最长的相等长度为nextt[18]=9(ababcabab),然后nextt[9]=4(abab),nextt[4]=2(ab), nextt[2]=0。

注意,括号的字符串既可以理解为前缀,也可以理解为原字符串的前后缀的组合(由于之前前后缀的匹配带来的关系)。

kuangbin带你飞专题(第一部分)
hdu1711——kmp模板题题解
hdu1686——kmp经典变形题(求可重叠模式串出现次数)题解
hdu2087——kmp经典变形题(求不可重叠模式串出现次数)题解
hdu3746——循环节经典变形题(添加最少串形成循环节)题解
hdu1358——经典循环节问题(求循环次数并判断是否区间内有完整的循环节)题解
poj2406——经典循环节题(求循环次数并判断是否有循环节)题解
poj2752——next数组(前后缀问题+输出所有前后缀相等长度)题解
poj3080——好题+暴力+vector妙用+kmp题解
hdu2594——kmp理解+next性质题解
hdu3336——循环节+next数组性质题解

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值