KMP算法中的next数组代码详解

KMP算法

字符串匹配中,设主串指针为i,匹配串指针为j,则KMP算法是在匹配过程中,i不会回溯的算法。

next数组定义

维护一个next[j]数组,next[j] = 当j位置不匹配时,指针应该回溯到的位置,即s[i] != str[j]时,令j = next[j],再次判断s[i]和str[j]

next数组计算方法:
	串的前缀:包含第一个字符,且不包含最后一个字符的子串
	串的后缀:包含最后一个字符,且不包含最后一个字符的子串
	当第j个字符匹配失败,由0~j-1个字符组成的串记为str[0..j-1],则
		next[j] = str[0..j-1]的最长相等前后缀长度, next[0] = -1

next[j] == str[0…j-1]的最长相等前后缀长度证明

如上图,若已知从串为str,则主串和从串匹配到了图示位置,此时发生了匹配失败,设主串为s,则有s[i-j…i-1] == str[0…j-1],且s[i] != str[j], 若有next[j] = k,则

  • 因为要求i不回溯,所以j回溯后,应该仍旧能和主串进行匹配,即有 s[i-k…i-1] == str[j-k…j-1] == str[0…k-1] , 其中,str[0…k-1]为前缀,str[j-k…j-1]为后缀,即满足要求的next[j]应该满足 是str[0…j-1]的相等前后缀的性质

  • 在前后缀相同的情况下,应该尽量选取较大的k,即最长相等前后缀,保证不错过匹配的可能性(如s=“ABABABAC”, str = “ABABAC”,见下图)

因此,k = str[0…j-1]最长相等前后缀长度,即next[j] = str[0…j-1]最长相等前后缀长度。

在这里插入图片描述

next数组求解

  1. 令next[0] = -1

在这里插入图片描述

  1. 若已知next[0…j-1],求next[j],即求str[0…j-1]的最长相等前后缀

    已知next[j-1] == k_j-1,设k = k_j-1(因为k_j-1不太好表示),即next[j-1] = k , 则有:

     str[0..k-1] == str[j-k-1..j-2], 即 str[0..j-2]的 长度为k的前缀 == 长度为k的后缀, 且k为最长相等长度
    

在这里插入图片描述

1)若 str[k] == str[j-1], 则

  str[0..k] == str[j-k-1..j-1], 即 str[0..j-1] 的 长度为k+1的前缀 == 长度为k+1的后缀

因此,有 next[j] = k + 1 = next[j-1] + 1;

2) 若str[k] != str[j-1], 则

与后缀相等的前缀必不是以k结尾,需要再往前找相等长度前后缀。

令k0 = next[k],则有:
在这里插入图片描述

  str[0..k0-1] == str[k-k0..k-1], 即 str[0..k-1]的 长度为k0的前缀 == 长度为k0的后缀
  又已知next[j-1] = k , 即str[0..k-1] == str[j-k-1..j-2]
  由此可以得到,
  	str[0..k0-1] == str[k-k0..k-1] == str[j-k-1..j-k+k0-2] == str[j-k0-1..j-2]

再次判断,str[k0]是否与str[j-1]是否相等:

  1. 若str[k0] == str[j-1], 则next[j] = k0+1

    str[0..k0] == str[j-k0-1..j-1], 即str[0..j-1]的 长度为k0+1的前缀 = 长度为k0+1的后缀
    
  2. 若 str[k0] != str[j-1],再令 k’ = next[k0],依次循环下去。

  3. 若k’ == -1, 则next[j] = 0

next数组求解代码

public int[] getNext(String str){
    int n = str.length();
    int[] next = new int[n];
    next[0] = -1;

    for(int j = 1;j < n;j++){
        int k = next[j-1];
        while (k >= 0 && str.charAt(k) != str.charAt(j-1)){
            k = next[k];
        }
        next[j] = k + 1;
    }
    return next;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值