字符串匹配算法(KMP)

两个字符串A、B,在A字符串中查找B字符串(分别长为m,n),如果找到了,返回B字符串在A字符串中第一次出现的下标。

暴力的方法是在A[k],从k=0开始对比B字符串汇中的字符,如果失败了,那么k++,时间复杂度为mn
这种算法忽略的B字符串中特性,也就是说,只向后移动一位对就开始对比,实际中,可能需要移动的位数更多。

所以,需要先找一下规律,当在A[k]出对比B字符串失败后,应该向后移动多少位(也就是A[k+?]),开始对比?

引入一个概念:字符串前后缀最大相同长度
也就是一个字符串,如果前面n位和后面n位字符完全相同(顺序一样,不是镜像)。那么n就是字符串前后缀最大相同长度(名字不要在意,别的都是公共部分,我觉得别扭)。假设该字符串长为m,那么n的取值范围为0~m,也就是说这个前缀和后缀可以重叠,可以完全重叠。

假若,在A[k]中匹配B,在B[h]开始不同,那么直接向后移动B字符串前h位的前后缀最大长度(以后都称为next[h])既可。
当在B[h]匹配失败后,B[h-next[k]]B[h]的字符串(前缀)与B[0]B[next[k]]的字符串(后缀)是相同的,在A中对应的位置也是相同的。此时,只需要将B[0]移动到B[h-next[h]]位置,移动后,B在A中的前next[h]位是已经匹配好的。所以不需要再从B[0]开始匹配了。

为什么可以跳过这么多的位置,重要的要理解next[]的构造过程。

构造过程之这样子的:
next从下标1开始存储,因为B从开始到结束,子字符串长度为0没意义。当子字符串长度为1是,next[1]=1

假设当子字符串长度为k时(也就是B[k-1]以及之前),前后缀最大相同长度为m

子字符串长度为k+1:当B[k]=B[m+1]时 ,(B[k]为之前的后缀之后的第一个字符,B[m+1]是前缀之后的第一个字符,如果相等,就是前后缀最大相同长度+1),所以next[k+1]=next[k-1]+1

不想等时:开始比较B[m]位和B[k]位,如果相等也就是,之前的后缀和前缀,在后面有公共部分。其相同长度可能为next[m]+1,因为B[m-1]子字符串的前缀的后一位可能不等于B[m]。如果相等,那么next[k]=next[next[k]]+1
否则,再比较B[m-1]B[k],同上。

#include <vector>
#include <iostream>
#include <string>

using namespace std;


vector<int> calNext(const string &str)
{
    int len = str.size();                                   //总长度
    vector<int> next(len + 1);
    next[2] = 0;
    int sublen;                                             //要计算next的子字符串
    for(int i = 2; i < len ; i++)  
    {
        sublen = i+1;
        if(str[i] == str[next[sublen-1]] )                  //sublen - 1 上一个子字符串长度
        {                                                   //next[sublen - 1] 上个子字符串前缀的后一位
            next[sublen] = next[sublen-1] +1;
        }else{
        
        //B子字符串前缀右移,以此比较其前缀与str[i]
        for(int j = next[sublen-1] -1  ; j >= 0; j--)         
        {
            //比较子字符串前缀与str[i]
            if(str[next[j]] == str[i])
            {
                //如果子字符串前缀后一位与str[i]相同
                //那么比较以前缀为子字符串的前缀的后一位与str[i]
                //这样就还是比较了一个子字符串的前缀和后缀与待计算next的字符
                if(str[next[next[j]-1]] == str[i])
                //上面的-1,是将长度转化为下标,next中的都是下标
                {
                    next[i+1] = next[next[j]]+1;
                    break;
                }
            }
        }
        }
    }
return next;
}

int main()
{
    string str("ABCKACBACLABKJ");
    str="abaabcaba";
    vector<int> next = calNext(str);

    for(int i = 1 ;i < next.size();i++)
    {
        cout<<next[i]<<" ";
    }
    cout<<endl;
}

这就是next的构建,其基本思想是:
比较一个子字符串的前缀后一位和后缀后一位与待计算next字符,如果一样,那么待计算字符的next值就是子字符串的next值+1.
现在next中存储的就是每次跳转的时候额外跳转的歩数,同时也是跳转以后搜索的起点。

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

推荐阅读更多精彩内容