KMP(二) 模式匹配算法实现


概述:本文主要在代码层面上分析KMP的实现过程,如果您还不了解KMP的推导过程,请参考
KMP(一) 模式匹配算法推导 --《部分匹配表》
KMP(二) 模式匹配算法实现
KMP(三) 字符串快速匹配示例
字符串快速匹配Demo下载


一.next 数组计算:

KMP(一) 模式匹配算法推导 --《部分匹配表》中给出的部分匹配值计算方式,用代码貌似并不容易实现(孤陋寡闻了,目前我还没想出来好的计算方式);我们再来看一种利用next 数组值实现KMP的方式;首先我们要计算next数组;
同样针对长度为 L 的字符串 T 先给出一个约定:
前缀: 字符串T[0~K], 0 <= k <=(L -1);
后缀:字符串T[K~L],0 <= k <=(L -1)
注意:此处没有组合的概念,表达不同于KMP(一) 模式匹配算法推导 --《部分匹配表》中的前缀和后缀
在给出next计算方式之前,结合KMP 算法推导中部分匹配值的计算方式,先看示例:

S = "ABAABAA"; 对应下表记为 i, 计算其next 数组;

  • i=0, 字符A前面没有字符串,默认约定next[0] = -1;
  • i = 1,字符B前面字符串"A",前缀和后缀都不存在,默认约定 next[1] = 0;
  • i = 2,字符A前面字符串"AB", 前缀后缀不纯在相等,则next[2]=0;
  • i = 3,字符A前面字符串"ABA",前缀和后缀相等部分为A,则next[3]=1;
  • i = 4,字符B前面字符串"ABAA",前缀和后缀相等部分为A,则next[4]=1;
  • i = 5,字符A前面字符串"ABAAB",前缀和后缀相等部分为AB,则next[4]=2;
  • i = 6,字符A前面字符串"ABAABA",前缀和后缀相等部分为ABA,则next[4]=3;
    即: next[L] = [-1,0,0,1,1,2,3];
    所谓的next数组就是求字符S[i]前面字符串中前后缀相等的最大长度而已。

二.next数组的作用:

与部分匹配值作用一致,next数组就是保存着当主串和模式串不匹配时,接下来与主串P[j]字符比较的模式串S[i]的位置,即S[i']=S[next[i]]。如:

主串: P = "ABABAABBB"; 下标以 j 表示
模式串(字串): S = "ABAABAA"; 下标以 i 表示

P[0~2] = S[0~2], P[3] != S[3],则比较到下标为 3 的位置时,按照KMP的原理,此时应将模式串 S 的待比较位置 移至 i = next[3] ,即S[next[3]]位置;
抽象图表示如下:

图片引用来自CSDN一位大神,一时找不到来源了,抱歉

看到此处,基本已经了解next 数组的作用,以及KMP结合next的基本实现原理;下面看code;

三. next数组的代码实现

前面有对next求解的过程,然而那只是为了理解next的含义,真正算法编程却无法那样直观求出,那该如何求解next数组呢?这里用到了类似并查集的算法。
主串:abababbbab

  • 首先next[0]=-1,next[1]=0;
  • 之后每一位j的next求解:
  • 比较j-1字符与next[j-1]是否相等,
  • 如果相等则next[j]=next[j-1]+1,
  • 如果不相等,则比较j-1字符与next[next[j-1]]是否相等,
  • 如果相等则next[next[j-1]]+1,
  • 如果不相等则继续以此下去,直到next[…]=-1,则next[j]=0.

通俗易懂的话来说就是你要求解当前位的next值,则看前一位与前一位next所在位字符是否相等,相等则当前位的next等于前一位next+1,如果不等就找前一位next的next与前一位比较,如果相等,当前位的next等于那个与前一位字符相等的字符所在位置+1,如果还是不相等,则以此类推,直到出现比较位为next[]=-1,那当前位的next就等于-1。
然而在算法求解的时候,我们应该这样去理解,求解下一位的next等于当前位与当前位的next比较。算法如下:


-(NSArray<NSNumber *> *)getNextWithString:(NSString *)string{

    NSMutableArray<NSNumber *> * next = [NSMutableArray arrayWithCapacity:string.length];
    next[0] = @(-1);//初始化
    int j= 0,k= -1; //j:记录当前下标; k记录当前位的next

    while (j <  string.length - 1) {
        if (k== -1 || [string characterAtIndex:j] == [string characterAtIndex:k]) { // 比较当前(j)字符与当期位next处字符是否相等         
            next[++j] = @(++k); // 移动下标,并求解下一位的next;
        }else{
            k = [next[k] intValue]; // 回溯当前位的next
        }
    }
    return next;
}

四.KMP 算法的实现

根据上述next数组的用法,编写KMP实现代码如下:

-(NSArray<NSString *> *)indexKMPwithParentString:(NSString *)pString andSubstring:(NSString *)sString{
    NSArray *next = [self getNextWithString:sString]; // 计算next数组
    int index_s = 0 ,index_p = 0;    //标记 pString 和 sString 的当前比对位置
    NSMutableArray<NSString *> *ranges = [NSMutableArray array]; //用于保存匹配结果的range位置

    while ((index_p < pString.length) && (index_s < sString.length)) { // 一直比对至两个字符串结尾
        if ([pString characterAtIndex:index_p] == [sString characterAtIndex:index_s]) { // 当前比对位置的字符相等,则移动P和S继续下一位比对
            if ((index_s == sString.length - 1) && (index_p != pString.length -1)) { // 字串S比对至末尾,但是主串P未到末尾,即是说字串匹配成功,但是尚需确定主串的后续位置是否能匹配,故继续比对
                index_s = 0; //将字串S当前比对位置移至起始位置
                [ranges addObject:NSStringFromRange(NSMakeRange(index_p - sString.length + 1 , sString.length))]; //保存本次匹配的位置结果
                continue; // 完成一次匹配,跳出本次循环 index_p 不再移动
            }
            index_p ++; // 向后移动P的当前位置
            index_s ++; // 向后移动S的当前位置
        }else{  //当前比对位置的字符不相等,则保持主串P位置不回溯,根据next数组回溯字串S
            if (index_s != 0) { //特别处理 next[0] = "-1"的情况
                index_s = [next[index_s] intValue]; // 根据next回溯当前字串S的比对位置
            }else{
                 index_p ++; //如果起始位置不匹配,则直接移动主串P
            }
        }
    }
    return ranges;
}

不才看了两天半,算是基本弄明白这个KMP吧,代码中注释我尽量说的白明些,显得比较啰嗦,记录一下,欢迎一起探讨;

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

推荐阅读更多精彩内容

  • 数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章、这篇文章、还有这篇他们写得非常...
    sunhaiyu阅读 1,735评论 1 21
  • 数据结构 第8讲 KMP算法 讲这个算法之前,我们首先了解几个概念: 串:又称字符串,是由零个或多个字符组成的有限...
    rainchxy阅读 1,294评论 0 3
  • 引言 字符串匹配一直是计算机科学领域研究和应用的热门领域,算法的改进研究一直是一个十分困难的课题。作为字符串匹配中...
    潮汐行者阅读 1,639评论 2 6
  • 文章大纲:1.KMP算法概念2.KMP算法中最核心的next[] 数组是如何生成的3.使用KMP算法 匹配字符串 ...
    柠檬乌冬面阅读 814评论 0 3
  • 歌手:张学友 她熄掉晚灯 幽幽掩两肩她sei丢满灯 邀邀银郎gin 交织了火花 拘禁在沉淀高鸡流佛发 亏刚拽沉丁 ...
    inke阅读 22,671评论 0 3