跳至主要內容

Z 函数(扩展 KMP)

大约 2 分钟

Z 函数(扩展 KMP)

题目难度
2223. 构造字符串的总得分和open in new window困难模板题
3031. 将单词恢复初始状态所需的最短时间 IIopen in new window困难模板题
3045. 统计前后缀下标对 IIopen in new window困难Z 函数 + 字典树

定义

对于个长度为 n 的字符串 s。定义函数 z[i] 表示 ss[i,n-1] (即以 s[i] 开头的后缀)的最长公共前缀(LCP)的长度。 z 被称为 s 的 Z 函数。特别地,z[0] = 0

国外一般将计算该数组的算法称为 Z Algorithm,而国内则称其为 扩展 KMP。

时间复杂度:O(n)

    private int[] z_function(int n, char[] s) {
        int[] z = new int[n];
        for (int i = 1, l = 0, r = 0; i < n; ++i) {
            if (i <= r && z[i - l] < r - i + 1) {
                z[i] = z[i - l];
            } else {
                z[i] = Math.max(0, r - i + 1);
                while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
            }
            if (i + z[i] - 1 > r) {
                l = i;
                r = i + z[i] - 1;
            }
        }
        return z;
    }

2223. 构造字符串的总得分和

public class Solution2223 {
    public long sumScores(String s) {
        int n = s.length();
        int[] z = z_function(n, s.toCharArray());

        long cnt = n;
        for (int i : z) {
            cnt += i;
        }
        return cnt;
    }

    private int[] z_function(int n, char[] s) {
        int[] z = new int[n];
        for (int i = 1, l = 0, r = 0; i < n; ++i) {
            if (i <= r && z[i - l] < r - i + 1) {
                z[i] = z[i - l];
            } else {
                z[i] = Math.max(0, r - i + 1);
                while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
            }
            if (i + z[i] - 1 > r) {
                l = i;
                r = i + z[i] - 1;
            }
        }
        return z;
    }
}

3031. 将单词恢复初始状态所需的最短时间 II

public class Solution3031 {
    public int minimumTimeToInitialState(String word, int k) {
        int n = word.length();
        int[] z = z_function(n, word.toCharArray());

        for (int i = 0; i < n; i += k) {
            if (i + k < n && z[i + k] + (i + k) >= n) {
                return (i + k) / k;
            }
        }
        return (n + k - 1) / k;
    }

    private int[] z_function(int n, char[] s) {
        int[] z = new int[n];
        for (int i = 1, l = 0, r = 0; i < n; ++i) {
            if (i <= r && z[i - l] < r - i + 1) {
                z[i] = z[i - l];
            } else {
                z[i] = Math.max(0, r - i + 1);
                while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
            }
            if (i + z[i] - 1 > r) {
                l = i;
                r = i + z[i] - 1;
            }
        }
        return z;
    }
}

3045. 统计前后缀下标对 II

public class Solution3045 {
    public long countPrefixSuffixPairs(String[] words) {
        TrieNode root = new TrieNode();
        long ans = 0;
        for (String word : words) {
            int n = word.length();
            int[] z = z_function(n, word.toCharArray());
            z[0] = n;

            TrieNode node = root;
            for (int i = 0; i < word.length(); i++) {
                int idx = word.charAt(i) - 'a';
                if (node.child[idx] == null) {
                    break;
                }
                node = node.child[idx];
                if (i + 1 == z[n - 1 - i]) {
                    ans += node.cnt;
                }
            }

            // insert
            node = root;
            for (char c : word.toCharArray()) {
                int idx = c - 'a';
                if (node.child[idx] == null) {
                    node.child[idx] = new TrieNode();
                }
                node = node.child[idx];
            }
            node.cnt++;
        }
        return ans;
    }

    static class TrieNode {
        TrieNode[] child;
        int cnt;

        public TrieNode() {
            this.child = new TrieNode[26];
            this.cnt = 0;
        }
    }

    private int[] z_function(int n, char[] s) {
        int[] z = new int[n];
        for (int i = 1, l = 0, r = 0; i < n; ++i) {
            if (i <= r && z[i - l] < r - i + 1) {
                z[i] = z[i - l];
            } else {
                z[i] = Math.max(0, r - i + 1);
                while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
            }
            if (i + z[i] - 1 > r) {
                l = i;
                r = i + z[i] - 1;
            }
        }
        return z;
    }
}

(全文完)