kmp算法 next[]數(shù)組的兩種求法

next數(shù)組兩種求法

image.png

一、求法的文字描述

(1)第一種求法:根據(jù)前一個字符的next值求字符串記作 p;next 數(shù)組記作 next;

約定:

  • 下標(biāo)從 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])為下標(biāo)的值(即 p[next[i-1]])是否相等

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

若不等,則繼續(xù)往回找,檢查

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

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

若不等,則繼續(xù)往回找,直到找到下標(biāo)為 1 還不等(即字符串第一個元素),直接賦值 next[i] = 1

(2)第二種求法:根據(jù)最大公共元素長度求

首先附上講解的博文地址,里面有詳細(xì)講解

  • 1)算出每一個字母前綴后綴的最大公共元素長度
  • 2)最大公共元素長度整體向后移動一個長度,最前面的元素值填 -1,即為 next 數(shù)組的第一版本
  • 3)(如果你需要的 next 數(shù)組第一個值為 -1,這步就可以省略了)next 數(shù)組的每一個值分別+1,即求得 next 數(shù)組。

前綴后綴的最大公共元素長度

  • 前綴:即從第一個字母開始往后看到最后一個字母(不包括)為止的字符串的以第一個字母開頭的子串(比如 "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

二、實例

現(xiàn)在求字符串 P = "ababaaababaa"

(1) 對于上面的第一種解法

  1. 初始化
P a b a b a a a b a b a a
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1

2)求下標(biāo)為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]] ,但是此時已經(jīng)回溯到了第一個元素
  • ∴ 直接P[3] = 1 ;
P a b a b a a a b a b a a
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1

3)求下標(biāo)為 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
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2

4)求下標(biāo)為 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
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3

5)求下標(biāo)為 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
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4

6)求下標(biāo)為 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]] 并且現(xiàn)在還沒有回溯到第一個,繼續(xù)
  • next[next[7-1]] = next[4] = 2;
  • P[next[next[7-1]]] = P[2] = 'b';
  • P[7-1] != P[next[next[7-1]]] 并且現(xiàn)在還沒有回溯到第一個,繼續(xù)
  • 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
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2

7)求下標(biāo)為 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]] 并且現(xiàn)在還沒有回溯到第一個,繼續(xù)
  • 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
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2

8)求下標(biāo)為 9 的字符的 next 值

  • 推導(dǎo)過程同4) => next[10] = next[10-1] + 1 = 4 ;
P a b a b a a a b a b a a
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2 3

9)求下標(biāo)為 10 的字符的 next 值

  • 推導(dǎo)過程同4) => next[10] = next[10-1] + 1 = 4 ;
P a b a b a a a b a b a a
下標(biāo) 1 2 3 4 5 6 7 8 9 10 11 12
next 0 1 1 2 3 4 2 2 3 4

10)求下標(biāo)為 11 的字符的 next 值
推導(dǎo)過程同4) => next[11] = next[11-1] + 1 = 5 ;

P a b a b a a a b a b a a
下標(biāo) 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)求下標(biāo)為 12 的字符的 next 值
推導(dǎo)過程同4) => next[12] = next[12-1] + 1 = 6;

P a b a b a a a b a b a a
下標(biāo) 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 數(shù)組的第一版本

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

三、代碼實現(xiàn)

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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容