串 - KMP

1.引子

`求子串的在一个长串中的定位操作通常叫做串的模式匹配`
传统的暴力匹配如上图,当遇到一个失配的地方,长串的下一轮的开始匹配的位置就要回溯,
但你要仔细观察的话,就会发现第2 3步其实是多余,直接可以开始第4步开始, 这就引出了我们今天要讲的KMP算法

1.KMP的思路是 在字符失配的情况下,长串不回溯,通过移动模式串(短串)的位置来进行匹配
2.核心是求出一个next数组,数组里面存放的是当前字符失配的情况下,字串需要向右移动的位置 

对于如何求next数组以及next数组的推导比较晦涩难懂 我拜读了很多文章后,感觉此篇文章利用块对称性讲解的比较透彻,记录下来供大家学习,传送门 同时我自己也记录一下 加深理解

2.KMP

KMP算法是在模式串字符与主串字符匹配失配时,利用已经匹配的模式串字符子集的最大块对称性,让模式串尽量后移的算法。
这里有3个概念:失配,已经匹配的模式串子集,块对称性

1.失配和隐含信息
在模式串的字符与主串字符比较的过程中,字符相等就是匹配,字符不等就是失配;
隐含信息是,失配之前,都是匹配。
在主串S[0,100]中查找模式串P[0,6],从下标0开始查找,在下标为5的位置失配,记为P[0,5]失配,则有
P[5]!=S[5],又有S[0,4]=P[0,4] 则P[0,4]都是匹配的!

2.已经匹配的模式串子集
模式串是P[0,6],而P[0,4]都是匹配的,所以,已经匹配的模式串子集有
Pcs={ P[0,4],P[0,3],P[0,2],P[0,1],P[0] }

3.块对称性, 由于比较重要 单独开一栏
2.1 什么是块对称性?

块对称性,就是字符串前缀,后缀重叠;
比如: a b c d a b c
前缀:除了最后一个字母外,所有的前缀子集;
如: a,ab,abc,abcd,abcda,abcdab
后缀:除了第一个字母外,所有的后缀子集;
如: bcdabc,cdabc,dabc,abc,bc,c
这里前缀abc和后缀abc重合

可以把这个重合看做,相对于绿块对称,所以叫它块对称性
块对称有很多种;比如:


2.2 块对称性有什么特点?

特点:拥有块对称性的字符串至少有2块对称重合的的部分;
分析,对称是修饰,重合是关键。而且重合的是前缀和后缀。

模式串如图,如果模式串和主串Str匹配的过程中,在l这失配即P[0,7]失配,你会怎样?
分析,
1,模式串的P[0,6]和主串放入S[0,6]是完全匹配的
2,P[0,6]串是块对称的!
因为P[0,6]刚好有块对称性,我可以把前缀abc移动到后缀abc的位置,然后让d与主串去匹配,这样就利用快对称性了


总结,可以在P[7]失配时,看失配字符的最大前缀P[0,6]是否有块对称性,如果有,我们就可以向右移动模式串,让左边的重合前缀移动到右边的重合后缀,再让模式串和主串比较

2.3 利用最大块对称性?

KMP是在模式串与主串匹配失配时,利用已经匹配的模式串子集的最大块对称性,尽量让模式串右移!这里的利用最大块对称性是什么意思?
这里利用最大块对称性意味着可能发生递归!


KMP算法会预先计算出模式串所有前缀子集中哪些前缀有块对称性,在这些有块对称性的前缀的后一个字符失配时,利用其块对称性;

比如本例中P[0,6]有块对称性,那么在P[0,7]也就是l失配时,
会先利用P[0,6]的块对称性,即P[0,2]和P[4,6]相遇于字符P[3]块对称,
如果不行,会看P[0,2]块对称重合的部分有没有块对称性,
有,就利用;以此类推,一直递归到没有块对称性为止。

2.4 块对称长度的意义-编程

第一次移动中,3是什么?块对称重合长度,也是下次开始比较的位置!
第二次移动中,1是什么?块对称重合长度,也是下次开始比较的位置!


3.next 数组的推导 - 计算块对称性

单独的块对称性是没有意义的,块对称性必须结合上失配,才能利用块对称性!
所以,应该计算出Pattern所有前缀子集失配时的块对称性!放到一个叫next[]数组的地方!
如何计算呢?
next数组是计算失配时的块对称性,
1.当第1个字符失配时,压根就没有前缀后缀的说法,所以有next[0]是不存在块对称性的,记为next[0]=-1;
2.当第2个字符失配时,它的子集只有1个字符,也是没有前缀后缀,没有块对称性,所以记为next[1]=0;
再看图,对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,则有next[j] = k。
next[j] = k代表了什么呢?
代表在Pj之前,有长度为k的块对称性,有2个长度为k的重合部分。

总结一下,前提条件如下:

1.next[0]是不存在的,next[1]=0;
2.对于下标值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,则有next[j] = k。

next[]数组是从0开始被初始的,如果我们能推导出next[j+1] = 什么,是不是就可以计算出next[]数组? 是吧

下面来推导next[j+1]



已知:

p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,==> next[j] = k
如果pk与pj匹配,则有p0 p1, ..., pk-1,pk = pj-k pj-k+1, ..., pj-1pj,==> next[j+1] = k+1;
原来有2个长度为k的对称重合部分,pk与pj匹配后,2个长度为k对称重合的部分又有了1对字符重合,所以有next[j+1]=k+1;
再看图,next[j]=k,当pj失配时,下一次用pk去和主串匹配;所以next[j]的实际意义是,当pj失配时,下一次应该用哪个字符去和主串匹配!!

3. next[ ]数组的值就是当次失配时,下一次匹配的位置!

如果pk与pj不匹配,next[j+1]=?
next[j+1]的实际意义是,p[0,j+1]的pj+1失配时,p[0,j]的块对称重合长度,也是下一次匹配时应该用模式串的哪个字符与主串匹配,那个字符的下标就是next[j+1]。

下一次用哪个字符比较呢?


设a1=p0 p1,...,pk-1,a2=pj-k pj-k+1,...,pj-1;a1==a2
当pk与pj不匹配时,不能用a1替换a2,如图绿叉;
因为a2是离与主串最近的部分,所以这时候应该分析a2是否有块对称性,
如果a2有块对称性,那么a1也有块对称性,如图绿框;
所以,这时应该分析p[0,k]的块对称性,也就是next[k]。
设x1与x2关于绿框对称;x3与x4关于绿框对称;
那么把x1移动到x4的位置,是不是就可以最大利用上;
所以next[j+1]=next[k];

4. 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXSIZE 20 // 存储空间初始分配大小
typedef char String[MAXSIZE + 1];

void get_next(String T, int *next) {
    int i,j;
    i = 1;
    j = 0;
    next[1] = 0;
    while (i < T[0]) {
        if (j == 0 || T[i] == T[j]) {
            ++i;
            ++j;
            next[i] = j;
        } else {
            j = next[j];
        }
    }
}

int Index_KMP(String S, String T, int pos) {
    
    int i = pos;
    int j = 1;
    int next[255];
    get_next(T, next);
    
//打印next数组
    int k = 0;
    while (k<=T[0]) {
        printf("the nextArr[%d] is %d\n",k,next[k]);
        k++;
    }
    
    while (i <= S[0] && j <= T[0]) {
        if (j == 0 || S[i] == T[j]) {
            ++i;
            ++j;
        } else {
            j = next[j];
        }
    }
    if (j > T[0]) {
        return i-T[0];
    } else {
        return 0;
    }
}



#pragma mark 使用
void KMP_Test() {
    String s1 = {11,'a','b','c','d','g','a','b','d','e','f','a'};
    String p1 = {5,'a','a','a','b','a'};

   int index = Index_KMP(s1, p1, 0);
   printf("the index is %d\n",index);
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343