KMP 模板+简单讲析 【HDU2087】 剪花布条

来源:互联网 发布:网络尖刀 合作 编辑:程序博客网 时间:2024/05/22 06:17

KMP
处理字符串匹配,暴力枚举O(n*n)是很好写的,但是很多题目接受不了这样的时间复杂度,然后KMP算法诞生了。

举个例子先:
模式串:abb
匹配串:ababb
匹配到第三个字符时失配了,如果是暴力匹配这时我们又会用模式串的第一个a来匹配匹配串的第二个b,这显然是很多余的操作,因为不可能匹配成功。这样浪费了巨量的时间,KMP算法则是用O(n)的时间来处理一下模式串,从而使匹配更有效率。

那怎么匹配呢?
举个例子再:
模式串:abaab(我们将这个字符串由0~4标号)
我们设置一个fix数组,代表如果这一位失配了,将要跳到哪一位继续匹配。
第0个字符:如果第0位就失配了,那没有办法了,只能再从0开始匹配,fix[0]=0;
第1个字符:和第0位一样,如果失配就回到0,fix[1]=0;
第2个字符:如果这一位失配了怎么办呢?

abaab
baab
..↑it’s me
这样肯定不行

abaab
aab
↑it’s me
那只能这样了,fix[2]=0;

第3个字符:如果这一位失配了怎么办呢?
abaab
baab
…..↑it’s me
不行

abaab
aab
..↑it’s me
要是这么跳的话,前面的就一样了,那如果我失配了,就让匹配串跟这一位配吧。
fix[3]=1;

第四个字符:如……?
……
abaab
ab
..↑it’s me
fix[4]=1。

以上是思考过程,重点是,如果我失配了,我就往前跳,但要求我跳完之后和模式串的前缀相同。
具体怎么实现呢?
按照刚刚的思考方式,如果当前位置失配了怎么办呢?
先想想上一位失配了,那它就会往回跳,保证它前面的一部分是这个模式串的前缀,如果这时恰好它跳到位置和它正好相同,那当前位置就找到了失配时应该跳的位置。

我说的不清晰,可以参照代码理解(注意事项在代码后):

 fix[0]=0; fix[1]=0; for(int i=1;i<lenc;i++) {      int j=fix[i];      while(j && c[j]!=c[i]) j=fix[j];      if(c[j]==c[i]) fix[i+1]=j+1;      else           fix[i+1]=0; }

注意事项:
1、上面的代码是用这一位来推下一位的状态,和之前写的可能不太相同,但是思想是一样的(c数组是模式串);
2、fix[0]和fix[1]最开始时就赋值为0;
3、如果j跳到0要结束循环(否则会死循环);
4、赋值时赋的是j的下一位。
KMP的模板很短也很好背,但是比较难理解,多写多想会好一些。

最后就是匹配了。
匹配就比较简单了,正常暴力匹配,但是失配时把模式串的指针往前跳继续与匹配串匹配就好了,当匹配成功的长度与模式串长度相同时,把答案+1就可以了。

匹配代码如下(s数组是匹配串,注意事项在代码后):

int j=0;for(int i=0;i<lens;i++){    while(j && c[j]!=s[i]) j=f[j];    if(s[i]==c[j]) j++;    if(j==lenc) {ans++; j=0;} }

注意事项:
1、j跳到0时要结束循环(理由同上);
2、当j==lenc的时候不一定要吧j赋值成0,这道题需要赋值成0,其他的题可能需要往前跳(当子串可以重复时)。

【HDU2087 剪花布条】
这是一道KMP练手裸题。

题目大意:
给定匹配串和模式串,问有匹配串中有多少个子串与模式串相同(子串不能重叠)。

题目分析:
KMP

代码:把上面两个拼起来就可以了。

1 0
原创粉丝点击