Z函数总结
记录了一个后缀和原串的LCP长度的信息。有点和后缀扯上关系了。
也有人叫它扩展KMP,但其实和KMP没有太大关系。
对于字符串\(s\),定义\(z_i\)表示\(suf_{s,i}\)与\(s\)的最长公共前缀(LCP)的长度。我们可以\(O(n)\)求出\(z_i\)。
类似KMP,我们同样递推,利用已经求出的信息降低复杂度。
根据定义,对于任意的\(i\)都有\(s_{1\dots z_i}=s_{i\dots i+z_i-1}\)。这类似于border,可以用来加速。
考虑维护最靠右的匹配段\(s_{i\dots i+z_i-1}\),记作\([l,r]\)。特别地,\(z_1=|s|\)没有意义,不算入匹配段中。于是从\(2\)开始,初始化\(l=r=0\)。
计算\(z_i\)时,讨论一下:
-
若\(i\le r\),\(s_{1\dots r-l+1}=s_{l\dots r}\Rightarrow s_{i-l+1,r-l+1}=s_{i,r}\),那么就有\(z_i\ge \min(r-i+1,z_{i-l+1})\),初始化后再暴力拓展。
-
若\(i>r\),直接暴力拓展。
计算结束后更新\([l,r]\)即可。
看起来很暴力,但分析一下可以知道是\(O(n)\)的:
-
\(i>r\)时,暴力拓展一定使\(r\)变大。
-
\(i\le r\land z_{i-l+1}> r-i+1\)时,暴力拓展也会让\(r\)变大。
-
\(i\le r\land z_{i-l+1}\le r-i+1\)时,不会暴力拓展。
由于\(r\)最多从\(0\)变大到\(|s|\),所以复杂度是\(O(n)\)的。
-
对于这道题luogu Z函数模板的第二问,用推导\(z\)函数的思路推一下即可。
-
循环移位后与原串比大小:首先想到\(s\)与\(t\)比大小只需比\(LCP(s,t)\)后的第一个字符即可。循环移位可以将字符串复制一份接在后面,然后上\(z\)函数推即可。

浙公网安备 33010602011771号