📅  最后修改于: 2023-12-03 15:22:46.381000             🧑  作者: Mango
加权字符串为回文的树(PPTree)是一种用于回文串统计的数据结构,常见于字符串领域。PPTree 以字符串中每个字符为节点建立一棵树,每个节点代表的是以该字符为中心的回文串。通过构建并遍历 PPTree,可以快速统计出字符串中所有的回文串(包括重复的),以及每种回文串出现的次数。
本文将介绍如何构建 PPTree,并通过实例演示如何统计字符串中的回文串数量。
PPTree 的构建原理,在一定程度上类似于扩展 KMP 算法。不同之处在于,PPTree 在遍历字符串的过程中,会同时记录下以每个节点结尾的回文串数量。并且,PPTree 的节点可以根据已有信息自我扩展。
具体来说,PPTree 的构建会分为两个阶段:
节点的扩展则分为两种情况:
以下是一个基于 Java 实现的 PPTree:
public class PPTree {
private Node root;
private Node pivot;
private class Node {
private Map<Character, Node> children = new HashMap<>();
private Node link;
private int count;
private int start;
private int end;
}
public int build(String s) {
int count = 0;
root = new Node();
root.link = new Node();
pivot = root.link;
pivot.count = -1;
for (int i = 0; i < s.length(); ++i) {
insert(s.charAt(i));
count += pivot.count;
}
return count;
}
private void insert(char c) {
// search for the last node whose palindrome has the pivot as its suffix
while (true) {
int len = pivot.end - pivot.start;
if (pivot.start > 0 && len + 1 < pivot.link.end - pivot.link.start) {
pivot = pivot.link;
continue;
}
Node child = pivot.children.get(c);
if (child != null) {
pivot = child;
break;
}
// create a new node and try to expand its parent node
Node node = new Node();
node.start = pivot.end + 1 - 2 * len;
node.end = Integer.MAX_VALUE;
pivot.children.put(c, node);
pivot = pivot.link;
if (pivot == root) {
node.link = root.link;
} else {
while (true) {
pivot = pivot.link;
len = pivot.end - pivot.start;
if (len + 2 <= node.start - node.end) {
node.link = pivot.children.get(c);
break;
}
}
}
break;
}
// update nodes' counts
Node oldPivot = pivot;
while (true) {
int len = pivot.end - pivot.start;
Node suffix = pivot.link;
if (pivot.start > len) {
pivot = suffix.children.get(s.charAt(pivot.start - len - 1));
continue;
}
Node child = pivot.children.get(c);
if (child == null) {
break;
}
if (suffix == root.link || suffix.link.children.containsKey(c)) {
child.count += oldPivot.count + 1;
}
pivot = child;
}
pivot.count++;
pivot.end = BranchInf;
}
}
以下是 PPTree 算法的关键步骤:
insert
方法将该字符插入到 PPTree 中insert
方法会先在已有节点中搜索是否存在当前字符的节点,如果找到则直接更新节点信息,否则创建一个新节点并尝试扩展其父节点link
属性出发,沿着树上的 link
指针一直向上搜索,一直到找到或创建一个能够接收新字符的节点while
循环中完成的,其中的 pivot
变量代表当前回文串最右边的字符所在的节点设字符串长度为 $n$,字符集大小为 $k$,则 PPTree 算法的时间复杂度为 $O(nk)$。
如果使用时空双指针法,可以将空间复杂度优化到 $O(n)$。
以下是一个使用 PPTree 统计回文串数量的示例:
String s = "aabbaa";
PPTree ppt = new PPTree();
int count = ppt.build(s); // 10
PPTree 是一种快速统计回文串的数据结构,可以在时间复杂度 $O(nk)$ 的情况下完成统计。尽管与 Manacher 算法、SAM 等传统算法相比,PPTree 的实现和细节较为复杂,但在一些特定场景下,PPTree 仍然具有不可替代的优势,比如需要同时统计多种回文串类型、或者需要动态维护回文串数量时。