求字符串的next值的两种方法

这两天在研究关于字符串匹配的KMP算法,其中需要求串的next值,看了很多算法描述和代码,一开始有点绕,然后多看了几篇,慢慢就理解了。自己目前掌握的有两种求字符串next值的方法,下面用简单通俗的描述记录下来。

以字符串ababaaababaa(下文也称为字符串s)为例,其next值为011234223456,下面介绍两种求解方法,其中无论是字符串s或是next数组,下标均从1开始(注意不是0),即下标为1-12。且next前两个值默认设置为0,1:next[1]=0,next[2]=1。

一、前缀后缀匹配

1.通俗描述

若字符串长度为length,则next数组也有length个值,当求解第i个next值时(1 <= i <= length,其实next[1],next[2]可以不算的,因为无论怎么算都是0,1),取字符串s的前i-1个字符,再取这i-1个字符的最长前缀字符串front(前i-2个字符)和最长后缀字符串back(后i-2个字符),其实就是去掉这i-1个字符串的最后一个和最前一个。然后对front和back进行匹配,匹配到的字符串,必须是front的头,以及back的尾,(其实此处匹配的也可以理解成字符串的前缀集合和后缀集合的交集中最长的字符串)若匹配到的字符串长度为l,则next[i]=l+1,若没有匹配到,则next[i]为1。这个匹配部分这样讲有点难理解,看一下下面的求解过程就清楚了。

2.求解过程
  • 字符串s(ababaaababaa)长度为12,所以next数组长度也为12。
  • 默认next[1]=0,next[2]=1,不需要计算。
  • 求next[3],取字符串s前2个字符,即为ab,再求最长前缀字符串front为a,最长后缀字符串back为b,对front(a)和back(b)进行匹配,没有匹配到任何东西,所以匹配到的字符串长度为0,则next[3]=0+1=1。
  • 求next[4],取字符串s前3个字符,即为aba,再求最长前缀字符串front为ab,最长后缀字符串back为ba,对front(ab)和back(ba)进行匹配,只匹配到a这个字符串,(a为front:ab的头,a是back:ba的尾),所以匹配到的字符串长度为1,则next[4]=1+1=2。
  • 求next[5],取字符串s前4个字符,即为abab,再求最长前缀字符串front为aba,最长后缀字符串back为bab,对front(aba)和back(bab)进行匹配,只匹配到ab这个字符串,(ab为aba的头,ab是bab的尾),所以匹配到的字符串长度为2,则next[5]=2+1=3。
  • 求next[6],取字符串s前5个字符,即为ababa,再求最长前缀字符串front为abab,最长后缀字符串back为baba,对front(abab)和back(baba)进行匹配,只匹配到aba这个字符串,(aba为abab的头,ab是baba的尾),所以匹配到的字符串长度为3,则next[6]=3+1=4。
  • 。。。
  • 求next[12],取字符串s前11个字符,即为ababaaababa,再求最长前缀字符串front为ababaaabab,最长后缀字符串back为babaaababa,对front(ababaaabab)和back(babaaababa)进行匹配,只匹配到ababa这个字符串,(ababa为ababaaabab的头,ababa是babaaababa的尾),所以匹配到的字符串长度为5,则next[12]=5+1=6。
  • 因此,这样计算下来,next值为011234223456。

二、next值做字符串下标

1.通俗描述

字符串s的下标从1开始算,next数组下标也从1开始算,所以s每个字符都对应一个next值。next[1],next[2]还是设置为0,1。当求next[i]时,即求字符串s的第i个字符对应的next值,求next[i],首先获得前一个next值和对应的字符,即s[i-1]和next[i-1]。用next[i-1]作为s的下标,判断s[i-1]和s[next[i-1]]是否相同,如果相同,则next[i] = next[i-1]+1。否则,保留s[i-1]这个字符串(这个字符串是用来比较的),再用s[next[i-1]]这个字符串对应的next(我们这里成这个next值为new_next,不要和前面混乱了)来作为刚刚next[i-1]的作用。即判断s[i-1]和s[nex_next]是否一样,如果相同,next[i]=new_next+1,否则继续往起直到找不到相同的,则next[i]=1。这里有点难理解,所以下面我会详细讲一遍具体怎么求法,但是建议还是边画边看,理解起来才容易。

2.求解过程
  • 字符串s(ababaaababaa)长度为12,所以next数组长度也为12。
  • 默认next[1]=0,next[2]=1,不需要计算。
  • 求next[3],获得前一个next值(next[2]=1)和对应的字符(s[2]=b,注意,这个b就是用来一直比较的,不用变换),因为前一个next为1,所以判断s[1]和b(s[2])是否相同,因为不相同,继续往前,s[1]对应的next[1]为0,超出下标范围了,证明找不到,所以next[3]设置为1。
  • 求next[4],获得前一个next值(next[3]=1)和对应的字符(s[3]=a),因为前一个next为1,所以判断s[1]和s[3]的值,发现都为a,所以相同,所以next[4]等于前一个next+1,即为1+1=2。
  • 求next[5],获得前一个next值(next[4]=2)和对应的字符(s[4]=b),因为前一个next为2,所以判断s[2]和s[4]是否相同,发现都为b,所以next[5]等于上一个next+1,即为2+1=3。
  • 求next[6],比较s[next[5]]和s[5]是否相同,发现都为a,所以next[6]等于前一个next+1,即3+1=4。
  • **(重要)**求next[7],获得前一个next值(next[6]=4)和对应的字符(s[6]=a),因为前一个next为4,所以判断s[4]和s[6]是否相同,发现一个为b,一个为a,因此要继续往前。此时s[6]不用去变,一直用它来比较的。取s[4]对应的next(next[4]=2)来代替前一个next(即next[6]),所以比较s[2]和s[6]是否相同,不同,继续往前,此时s[6]还是不用去变,用s[2]对应的next值(next[2]=1)来替代next[4],所以判断s[1]和s[6]是否相同,发现都是a,所以next[7]等于next[2]+1,即1+1=2。
  • 求next[8],也是需要不断往前更新next值,直到next[2]=1,此时判断s[next[2]](即s[1]=a)和s[7]均为a,所以next[8] = next[2]+1,即1+1=2。
  • 之后求next[9~12],均是一步就想等,所以直接前一个next+1,这里不细算,分别是3,4,5,6。
  • 因此,这样计算下来,next值为011234223456。
  • 52
    点赞
  • 159
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
模式的next数组可以通过两种方法来计算:观察法和推理法。 1. 观察法: 观察法是一种直接观察模式中的重复子来计算next数组方法。具体步骤如下: - 首先,将next数组的首个位置的设为-1。 - 然后,从第二个位置开始,依次计算每个位置的next。 - 对于第i个位置,我们需要找到在s[i]之前的字符串中的重复子,并计算其长度。 - 重复子的长度可以通过观察模式中的重复部分来得到,即找到一个最长的相同前缀和后缀。 - 将这个最长相同前缀和后缀的长度赋给next[i]。 2. 推理法: 推理法是一种根据已知的next数组来推理下一个位置的next方法。具体步骤如下: - 首先,将next数组的首个位置的设为-1。 - 然后,从第二个位置开始,依次计算每个位置的next。 - 对于第i个位置,我们需要根据已知的next数组来推理出next[i]的。 - 根据已知的next数组,我们可以得到一个最长的相同前缀和后缀的长度,假设为k。 - 如果s[i]和s[k]相等,则next[i]的为k+1。 - 如果s[i]和s[k]不相等,则我们需要继续向前推理,找到一个更短的相同前缀和后缀的长度,直到找到一个相等的字符或者到达字符串的开头。 - 将这个最长相同前缀和后缀的长度赋给next[i]。 以上是模式的next数组的计算方法,可以根据个人喜好选择使用观察法或推理法来计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值