kmp算法 next[]数组的两种求法

next数组两种求法

image.png

一、求法的文字描述

(1)第一种求法:根据前一个字符的next值求字符串记作 p;next 数组记作 next;

约定:

  • 下标从 1 开始算,注意,不是从 0 开始算

  • 字符串长度 >2

  • 1)第一个字母的 next 值置 0 (nesxt[1] = 0),第二个字母的 next 值置 1(next[2] = 1)

  • 2)从第 3 个开始,计算第 i 个位置的 next 值时,检查

p[i-1]== p[next[i-1]] ?(即这两个值是否相等)

解释:第 i 个位置的前一个位置的值(即 p[i-1])与以该位置的next 值(即 next[i-1])为下标的值(即 p[next[i-1]])是否相等

若相等,则 next[i] = next[i-1] + 1

若不等,则继续往回找,检查

p[i-1]== p[next[next[i-1]]] ?

若相等,则 next[i] = next[next[i-1]] + 1

若不等,则继续往回找,直到找到下标为 1 还不等(即字符串第一个元素),直接赋值 next[i] = 1

(2)第二种求法:根据最大公共元素长度求

首先附上讲解的博文地址,里面有详细讲解

  • 1)算出每一个字母前缀后缀的最大公共元素长度
  • 2)最大公共元素长度整体向后移动一个长度,最前面的元素值填 -1,即为 next 数组的第一版本
  • 3)(如果你需要的 next 数组第一个值为 -1,这步就可以省略了)next 数组的每一个值分别+1,即求得 next 数组。

前缀后缀的最大公共元素长度

  • 前缀:即从第一个字母开始往后看到最后一个字母(不包括)为止的字符串的以第一个字母开头的子串(比如 "abab" 的前缀有a,ab,aba);

  • 后缀:即从最后一个字母开始往前看到第一个字母(不包括)为止的字符串的以最后一个字符为末尾的子串(比如 "abab" 的后缀有b,ab,bab);

  • 最大公共子串长度:也就是前缀和后缀拥有的相同子串的最大长度;

    以"abab"为例:

模式串的各个子串 前缀 后缀 最大公共元素长度
a 0
ab a b 0
aba a,ab a,ba 1
abab a,ab,aba b,ab,bab 2

二、实例

现在求字符串 P = "ababaaababaa"

(1) 对于上面的第一种解法

  1. 初始化
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1

2)求下标为3的字符的next值

  • P[3-1] = P[2] = 'b';
  • next[3-1] = next[2] = 1 ;
  • P[next[3-1]] = P[1] = 'a';
  • P[3-1] != P[next[3-1]] ,但是此时已经回溯到了第一个元素
  • ∴ 直接P[3] = 1 ;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1

3)求下标为 4 的字符的 next 值

  • P[4-1] = P[3] = 'a';
  • next[4-1] = next[3] = 1 ;
  • P[next[4-1]] = P[1] = 'a';
  • P[4-1] == P[next[4-1]] ;
  • ∴ next[4] = next[4-1] + 1 = 2 ;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2

4)求下标为 5 的字符的 next 值

  • P[5-1] = P[4] = 'b';
  • next[5-1] = next[4] = 2 ;
  • P[next[5-1]] = P[2] = 'b';
  • P[5-1] == P[next[5-1]] ;
  • ∴ next[5] = next[5-1] + 1 = 3 ;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3

5)求下标为 6 的字符的 next 值

  • P[6-1] = P[5] = 'a';
  • next[6-1] = next[5] = 3;
  • P[next[6-1]] = P[3] = 'a';
  • P[6-1] == P[next[6-1]];
  • 所以 next[6] = next[6 - 1] + 1 = 4;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4

6)求下标为 7 的字符的 next 值

  • P[7-1] = P[6] = 'a';
  • next[7-1] = next[6] = 4;
  • P[next[7-1]] = P[4] = 'b';
  • P[7-1] != P[next[7-1]] 并且现在还没有回溯到第一个,继续
  • next[next[7-1]] = next[4] = 2;
  • P[next[next[7-1]]] = P[2] = 'b';
  • P[7-1] != P[next[next[7-1]]] 并且现在还没有回溯到第一个,继续
  • next[next[next[7-1]]] = 1;
  • P[next[next[next[7-1]]] = 'a';
  • P[7-1] == P[next[next[next[7-1]]]];
  • 所以next[7] = next[next[next[7-1]]] + 1 = next[2] + 1 = 2
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2

7)求下标为 8 的字符的 next 值

  • P[8-1] = P[7] = 'a';
  • next[8-1] = next[7] = 2;
  • p[next[8-1]] = P[2] = 'b';
  • P[8-1] != P[next[8-1]] 并且现在还没有回溯到第一个,继续
  • next[next[8-1]] = 1;
  • P[next[next[8-1]]] = p[1] = 'a';
  • P[8-1] == P[next[next[8-1]]];
  • 所以next[8] = next[next[8-1]] + 1 = 2;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2

8)求下标为 9 的字符的 next 值

  • 推导过程同4) => next[10] = next[10-1] + 1 = 4 ;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2 3

9)求下标为 10 的字符的 next 值

  • 推导过程同4) => next[10] = next[10-1] + 1 = 4 ;
P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2 3 4

10)求下标为 11 的字符的 next 值
推导过程同4) => next[11] = next[11-1] + 1 = 5 ;

P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2 3 4 5

11)求下标为 12 的字符的 next 值
推导过程同4) => next[12] = next[12-1] + 1 = 6;

P a b a b a a a b a b a a
下标 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2 3 4 5 6

(2) 对于上面的第二种解法

image.png

1)算出每一个字母前缀后缀的最大公共子串长度

P a b a b a a a b a b a a
前后缀最大公共子串长度 0 0 1 2 3 1 1 2 3 4 5

2)最大公共子串长度整体向后移动一个长度,最前面的元素值填 -1,即为 next 数组的第一版本

P a b a b a a a b a b a a
前后缀最大公共子串长度 -1 0 0 1 2 3 1 1 2 3 4 5

三、代码实现

void getnext(seqstring *p, int next[])
{
    int i, j;
    next[0] = -1;
    i = 0; j = -1;
    while (i < p->length)
    {
        if (j == -1 || p->str[i] == p->str[j])
        {
            ++i;
            ++j;
            next[i] = j;
        }
        else
            j = next[j];
    }
    for (i = 0; i < p->length; i++)
        printf("%d ", next[i]);
}

四、验证

#include "stdio.h"
#include "stdlib.h"
#define MAXSIZE 100

typedef struct {
    char str[MAXSIZE];
    int length;
}seqstring;

void getnext(seqstring *p, int next[])
{
    int i, j;
    next[0] = -1;
    i = 0; j = -1;
    while (i < p->length)
    {
        if (j == -1 || p->str[i] == p->str[j])
        {
            ++i;
            ++j;
            next[i] = j;
        }
        else
            j = next[j];
    }
    for (i = 0; i < p->length; i++)
        printf("%d ", next[i]);
}

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

推荐阅读更多精彩内容