KMP算法

KMP算法的探索

KMP是字符串比对中一种高效的算法,在数据结构课堂上时间短很难完全理解。
于是课后我又自己摸索了一番,根据自己的理解独立编写了程序

规定

为了下文叙述方便先规定几个统一的说法:

  • 文本串:被比对的字符串;
  • 模式串:比对的模板字符串;
  • 指针i:指向文本串正在比对位置的指针;
  • 一系列指针:下文结合程序注释;
  • next数组、两种字符串下标均从1开始;

BF-算法

字符串比对中的暴力算法,没啥好说的,文本串逐位与模式串进行比较,不匹配则指针i回溯。例如:从i=1开始匹配,匹配到i=5时失败,回溯至i=2重新匹配。

next数组

关于KMP算法中最核心的思想蕴含在next[]数组中,next数组下标对应着字符串下标(下标从1开始,0位空置,至少我是空置)。顾名思义,next数组的值意义为本次“失配”后,下次匹配开始的位置(模式串的下标)

思想动画

图A:源自于B站视频

图B:源自于B站视频

从图A到图B:第六位A失配后,很明显模式串发生了向右的滑动。
滑动的依据:滑动前箭头左侧的模式串前缀“AB”与后缀“AB”完全一致,由于箭头左侧已经匹配成功,所以对应的文本串也是一样的。这时候向右滑动既保证与文本串匹配,又避免了(滑动后)箭头左侧重复匹配操作。
滑动位数:即next数组的值。图中若以next数组表示,即位next[3] = 3。

  • 两个‘3’均指模式串的第三位。翻译过来就是:当模式串中的第三位失配时,下一步从模式串的第三位继续匹配。
  • 进一步抽象:有数组 next[x] = y,意思是:当模式串中的第x位失配时,下一步从模式串的第y位继续匹配。

上述 next[x] = y ,x没什么好说的就是模式串下标,下一步匹配位置y是如何得到的呢?
根据上图可以稍微总结一下:失配位置(箭头)左侧模式串前后缀一致的长度L加1,就是y。这个也很好理解,相当于我长度为L的子串已经匹配好了,那么只需要从L+1处继续匹配就好啦。

图片源于B站

这张图意在说明,在我们找一致的前后缀(很多人称为公共前后缀)的时候,是可能发生重叠的。


如果以上内容你看懂了

你就理解了next数组的求法了:即找出失配位置前,模式串子串的公共前后缀的最大长度L,并从L+1位开始继续匹配。

void cal_nextValue()
    {
        string t = compstr;
        int m = 0; //记录能够匹配的公共前后缀的最大长度
        if (t.size() <= 0) return;
        next[1] = 0; //模式串第一位直接置0,该位置左侧子串长度为0
        if (t.size() <= 1) return;
        next[2] = 1;//第二位失配了,左侧最大公共前后缀只能为1
         //从第三位开始,在失配位置左侧字符串寻找最长子串为公共前后缀
        for (int i = 3; i < t.size(); i++)
        {
           // j为“左挡板”往右移动限定后缀,k为“右挡板”往左移动限定前缀
            for (int k = i - 2, j = 2; j < i; j++, k--)
            {
                int flag = 1;
                int j_ = j; //不改变挡板位置
              //后缀与前缀进行比较
                for (m = 1; m <= k && j_ < i; m++, j_++)
                {
                  //字符不同则跳出循环,移动挡板位置(缩小前后缀长度)重新比较
                    if (t[j_] != t[m])
                    {
                        flag = 0;
                        break;
                    }
                }
                if (flag) break;
            }                 
             //循环结束时,由于for循环种“m++”实际匹配的最长公共前后缀为m-1;
            next[i] = m; //从最长公共前后缀下一位继续匹配,即 m-1+1=m
        }

看下来我们会发现next数组的求解与文本串没有关系,任何模式串都可以求出自己的next数组


比较字符串

  • 其实next数组求解完了,比较字符串就很简单了。

  • 先定义i和j两个指针:i指向文本串;j指向模式串。

  • 第一次仍是逐一比较,相同则i、j均右移一位。当出现失配时,i不右移也不回溯,j根据next[j]的值移动,让移动后的模式串继续与第i位匹配比较。
    (当指针j回退至0时,指针i、j需同时++。即若主串的第i个字符和模式的第1个字符不等,应从主串的第i+1个字符重新匹配)
    循环结束后根据i、j指针位置即可判断

void cal_KMP()
    {
        cal_nextValue();
        if (tempstr.size() < compstr.size())
            return;

        int i = 1;
        int j = 1;
        while (i < tempstr.size() && j < compstr.size())
        {

            if ((tempstr[i] != compstr[j]) && next[j] != 0)
                j = next[j];
            else
            {
                i++;
                j++;
            }
        }
        if (j == compstr.size()) //匹配成功
        {
            result.didhave = true;
            result.pos = i + 1 - (compstr.size()-1);
        }
        else if (i == tempstr.size() && j != compstr.size()) //匹配失败
        {
            result.didhave = false;
            result.pos = -1;
        }
    }


教材简洁版

当我自己摸索完去看了眼教材,发现上面求next数组的程序好短

void get_next(SString T, int next[]){
  //T位模式串
  i = 1;
  next[1] = 0;
  j = 0;
  while(i < T[0]){
    if(j == 0 || T[i] == T[j]){
      ++i;
      ++j;
      next[i] = j;  
    }  
    else  j = next[j];
  }
}

T[0]应该是存放字符串长度的
有一说一我脑子不好挺难看懂。。放着有大佬会的也可以说说

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

推荐阅读更多精彩内容