学习 Z 函数

前言

  • 回旋镖终于是飞回来了。。。
  • 该学的都得学

\(Z\>\>function\)

  • \(Z\) 函数算法又叫 \(exKMP\),目的是在 \(O(n)\) 的线性复杂度内求出一个字符串对于每一个后缀的 \(LCP\) 长,将 \(Z\) 函数定义为一个字符串对于每一个后缀的 \(LCP\) 长,并定义 \(Z_0 = 0\)

\(LCP\):最长公共前缀

  • 例子:\(s=aaaaa,Z=[0,4,3,2,1]\)
  • 这个用枚举比较法可以 \(O(n^2)\) 求,但是我们要 \(O(n)\)
  • 给出 \(O(n)\) 计算的方法
  • 为了方便下标从 \(0\) 开始
  • 维护 \([l,r]\) 表示右端点最靠右的匹配段,使其初始均为 \(0\)
  • 如果 \(i\) 是 \(\le r\) 的,我们发现 \(s[i,r] = s[i-l,r-l]\),所以我们可以用之前的 \(z\) 函数的值求解,有 \(z_i \ge \min(z_{i-l},r-i+1)\),所以:
    • 当 \(z_{i-l} < r-i+1\),直接转移 \(z\) 函数,即 \(z_i = z_{i-l}\)
    • 否则使 \(z_i = r-i+1\) 并暴力扩展
  • 如果 \(i>r\),还是暴力做,求出 \(z[i]\)
  • 当 \(i+z_i-1>r\) 时,我们需要更新 \([l,r]\),使:\(l=i,r=i+z_i-1\)
  • 行了,就是写代码了
点击查看代码
int z[maxn];
void Z(string s)
{
    int len=s.size();
    int l=0,r=0;
    for (int i=1;i<len;i++)
    {
        if (i<=r && z[i-l]<r-i+1)
            z[i]=z[i-l];
        else
        {
            z[i]=max(0,r-i+1);
            while (i+z[i]<len && s[z[i]] == s[i+z[i]]) z[i]++;
        }
        if (i+z[i]-1>r)
            l=i,r=i+z[i]-1;
    }
}
posted @ 2026-10-04 16:35  msjing  阅读(5)  评论(0)    收藏  举报