KMP之next的优化

4.next数组求解算法优化

最后,来看一下上边的算法存在的缺陷。来看第一个例子:

显然,当我们上边的算法得到的next数组应该是[ -1,0,0,1 ]

所以下一步我们应该是把j移动到第1个元素咯:

不难发现,这一步是完全没有意义的。因为后面的B已经不匹配了,那前面的B也一定是不匹配的,同样的情况其实还发生在第2个元素A上。

显然,发生问题的原因在于P[j] == P[next[j]]

补充说明:这部分作者说的也比较清楚了。实际上对下面的代码if(p[++j]==p[++k]),我们注意是先自加,再使用。所以我们按照j不变的情况下解释下一步流程就是p[j+1]==p[next[j]+1];此时比较将无意义,因为p[next[k]+1]位就已经表示,就是k+1位和主串的i不相等,要移动的j下标为next[K+1],因为p[k+1]又等于p[j+1],也就是说比较j+1位和主串的i位是否相等时,也将要j移到next[K+1]位去;

所以我们也只需要添加一个判断条件即可:

public static int[] getNext(String ps) {

    char[] p = ps.toCharArray();

    int[] next = new int[p.length];

    next[0] = -1;

    int j = 0;

    int k = -1;

    while (j < p.length - 1) {

       if (k == -1 || p[j] == p[k]) {

           if (p[++j] == p[++k]) { // 当两个字符相等时要跳过

              next[j] = next[k];

           } else {

              next[j] = k;

           }

       } else {

           k = next[k];

       }

    }

    return next;

}
public static int[] getNext(String ps) {

    char[] p = ps.toCharArray();

    int[] next = new int[p.length];

    next[0] = -1;

    int j = 0;

    int k = -1;

    while (j < p.length - 1) {

       if (k == -1 || p[j] == p[k]) {
                ++j,++k;

           if (p[j] == p[k]) { // 当两个字符相等时要跳过

              next[j] = next[k];

           } else {

              next[j] = k;

           }

       } else {

           k = next[k];

       }

    }

    return next;

}

为什么优化next数组?

上面只是给出了部分匹配的结果,从第6步往后比i=4,j=4有s[i]!=t[j]

由于next[j]的指示还要进行i=4j=3,i=4j=2,i=4j=1,i=4j=0这4次比较。

实际上,模式中0,1,2,3和第4个字符都相等,因此不需要再和主串中第4个字符比较,因而可以将模式一气向右滑动5个字符,直接进行i=5j=0时的字符

按上述定义next[j]=k,模式串p[j]==p[k],则主串字符s[i]和p[j]比较不等,则s[i]和p[next[j]]也没有必要比较(因为next[j]=k,其中p[j]==p[k]),而直接和p[next[k]]进行比较

换句话说,此时next[j]应该和next[k]相同

这都是由:

辅助理解:严蔚敏《数据结构》81页,关于kmp的两种情况与严书不同的是,我存放数据从数组0号单元开始,严书0号单元存储长度从1号单元存储数据,这是区别要注意,所以下面的表述除数字外和严书相同)
 在求得模式的next函数之后,匹配可如下进行:假设以指针i和j 分别指示主串s和模式t中正待比较的字符,令i的初值为0,j的初值为0。若在匹配过程中 ,s[i]==t [j]   ,则 i 和j 分别增1,否则,i不变,而j退到next[j]的位置再比较,若相等,则指针各自增1,否则j再退到下一个next 值的位置,依次类推,直至下列两种可能:

一种是j 退到某个next 值(next [...next[j]...]])时字符比较相等,则指针各自增1,继续进行匹配;

另一种是j 退到值为-1(即模式的第一个字符“失配”,第一个字符对应数组的0号单元,所以数组0号单元失配则则退到0号单元的左边),则此时需将模式继续向右滑动一个位置,即从主串的下一个字符  s[i+1]   起和模式重新开始匹配。
 

 while (j < p.length - 1) {

       if (k == -1 || p[j] == p[k]) {
            ++j,++k;

           if (p[j] == p[k]) { // 当两个字符相等时要跳过

              next[j] = next[k];

           } else {

              next[j] = k;

           }

       } else {

           k = next[k];

       }

    }
//为什么while (j < p.length - 1) 因为 if (k == -1 || p[j] == p[k]) {++j,++k;循环体内++j,
而数组下标0--p.length - 1,要进入循环j最大p.length - 2,里面if (k == -1 || p[j] == p[k]) {++j,++k;循环体内才能进行++j
//为什么next[j]=k

               为什么next[j]=k;

          

为什么next[j]=next[k]?

因为if(j==-1||p[k]=p[j]),假设next[j]=k,进入++j,++k,上图红色p[k]==p[j](k++,j++后的j和k)

即next[j+1]=next[k+1]=k+1

tips:为了好理解,上面两句解释中的j和k为if(j==-1||p[k]=p[j]),

的值,

于是就有了next[j]=next[k]

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值