简介

z-box算法可用于普通KMP、扩展KMP,国外非常流行但是国内却几乎没有人用,这种算法在解决许多字符串问题时都比KMP要直观许多。

算法详解

对于一个字符串s,设它的长度为len

z[i]所表示的是s[i…len-1]与s[0…len-1]的最长公共前缀

如何求出z[i]数组?

递推即可

对于一个新的i,我们把i前面的所有z[j]看成一个区间[j,z[j]],记录右端点最大的区间[l,r],如果i>r,那么我们暴力和s[1..len]进行匹配,代码如下(注意下标从0开始):

if (i>r)
{
    int t=i,bg=0;
    while (b[t]==b[bg])
        t++,bg++;
    z[i]=bg;
    r=t-1;
    l=i;
}

如果 i<=r i <= r ,说明i一定被右端点最大区间[l,r]所覆盖,说明[l,r]和[0,r-l]是相同的,那么在[l,r]内,z[i]和z[i-l]也是相同的,类似manacher的思想,我们需要分类讨论一下:如果 z[il]<ri+1 z [ i − l ] < r − i + 1 ,即在i-l匹配到的区间长度小于r-i+1的话,那么我们直接赋值即可,如果不是,我们需要对r后进行暴力匹配,代码如下:

if (i<=r)
{
    if (z[i-l]<r-i+1) z[i]=z[i-l];
    else
    {
        int t=r+1,bg=r-i+1;
        while (b[t]==b[bg])
            t++,bg++;
        z[i]=bg;
        r=t-1;
        l=i;
    }
}

算法用途

字符串匹配

给出字符串n,m,求m在n中出现的次数以及出现位置

令s=m+’$’+n(这里的’\$’是指一个分隔符,不在题目所给出的字符集之内即可)

对s求出他的z数组,假设m的长度为lenm,n的长度为lenn,那么我们只需在z[lenm+1…lenm+lenn]里面找出值为lenm的位置即可。