next数组两种求法

算法 专栏收录该内容
7 篇文章 0 订阅

一、说明 
(1)看到网上同一个字符串求 next 数组的值有两种,一种是 -1 开头,一种是 0 开头,虽然有差别,但是以 0 开头的next数组的每一项都比以 -1 开头的next数组的对应项大1,所以,具体是以 0 开头还是以 -1 开头看需要吧,算法都是一样的.KMP 的原始论文 (K,M,P 三个家伙写的原文)中是以 0 开头的,所以下面的写法是以 0 开头的. 
(2)关于 next 数组的求法,网上能找到很多流行简洁的写法,也有很多文章对简洁代码讲解得非常细致,然而本文并不是对流行算法的剖析,而只是记录一下自己比较喜欢的计算方法,并用代码实现一下.

二、求法的文字描述

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

约定:

  • 下标从 1 开始算注意,不是从 0 开始算
  • 字符串长度 >2

1)第一个字母的 next 值置 0 (next[1] = 0),第二个字母的 next 值置 1(next[2] = 1) ; 
2)从第 3 个开始,计算第 i 个位置的 next 值时,检查

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

解释:第 i 个位置的前一个位置的值(即 p[i-1],记作 m)与以 m 的 next 值(即 next[i-1])为下标的值(即 p[next[i-1]],记作 n)是否相等,(看的懵懵的也没关系,后面会有例子)

  • 若相等,则 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)第二种求法:根据最大公共元素长度求 
首先附上讲解的博文地址,里面有详细讲解 
http://blog.csdn.net/v_july_v/article/details/7041827

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

三、实例

字符串 P =“ababaaababaa”

求解: 
(1)对应上面第一种求法 
1)初始化

Pababaaababaa
下标123456789101112
next01          

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 ;

Pababaaababaa
下标123456789101112
next011         

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 ;

Pababaaababaa
下标123456789101112
next0112        

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 ;

Pababaaababaa
下标123456789101112
next01123       

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

Pababaaababaa
下标123456789101112
next011234      

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’;番外(1) 
P[7-1] != P[next[next[7-1]]] && 但是此时还未回到第一个,继续 
next[next[next[7-1]]] = next[2] = 1 ; 
P[next[next[next[7-1]]]] = P[1] = ‘a’ ; 
P[7-1] == P[next[next[next[7-1]]]] ; 
∴ next[7-1] = next[next[next[7-1]]] + 1 = next[2] + 1 = 2 ;

Pababaaababaa
下标123456789101112
next0112342     

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]] = next[2] = 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

Pababaaababaa
下标123456789101112
next01123422    

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

Pababaaababaa
下标123456789101112
next011234223   

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

Pababaaababaa
下标123456789101112
next0112342234  

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

Pababaaababaa
下标123456789101112
next01123422345 

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

Pababaaababaa
下标123456789101112
next011234223456

(2)对应上面第二种求法 
1)算出每一个字母前缀后缀的最大公共子串长度(下一步会把最后一位移走,所以最后一位可以不算)番外(2)

Pababaaababaa
前后缀最大公共子串长度00123112345 

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

Pababaaababaa
next 数组第一版-100123112345

3)(如果你需要的 next 数组第一个值为 -1,这步就可以省略了)next 数组的每一个值分别+1,即求得 next 数组。

Pababaaababaa
next 数组第二版011234223456
  • 31
    点赞
  • 8
    评论
  • 86
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值