在多个串的字符串题中,每每会出现一类题须要用到某个子串是否在一些母串中出现。此时对于 \(\text{parent}\) 树的 \(\text{right}\) 集合而言,问题并不关心某个具体位置而只关心是否有某个 \(\text{endpos}\) 在指定母串中。数据结构
那么对于 \(\text{parent}\) 树上的来自同一个母串的节点而言,其对祖先的贡献都是能够替代的,并不须要重复标记其某个祖先 \(\text{right}\) 集合中是否存在一个 \(\text{endpos}\) 来自这个母串。测试
因而咱们维护来自这个母串的全部节点对 \(\text{parent}\) 树的贡献复杂度等价于全部来自这个母串的叶子节点在 \(\text{parent}\) 树上的并的大小。(此处叶子指的是来自这个母串且其子树内不存在来自这个母串节点的节点)spa
分析这些叶子在树上的并的大小的具体规模,有一个显然的上界就是 \(\sum_{i=1}^{n}2len_i-1\) ,其中 \(len_i\) 是第 \(i\) 个串的长度,对其建后缀自动机的节点数上界为 \(2len_i-1\) ,对 \(n\) 个串一块儿建能够获得这个显然的上界。字符串
观察这棵并起来的树的性质能够发现,这棵树上的每个节点的 \(\text{right}\) 集合都包含来自这个母串的 \(\text{endpos}\) ,而每一个节点的父节点都是该节点所表明串的一个后缀且至少长度减小 \(1\) 。那么考虑这个串每个前缀的最大贡献就是这个前缀的长度,因此能够获得另一个上界 \(len_i^2\) 。模板
假设这 \(n\) 个串的总长为 \(S\) ,那么 \(\sum_{i=1}^{n}2len_i-1\) 能够看作 \(2S\) ,因此对于每个母串都维护贡献的复杂度是 \(\sum_{i=1}^n \min(2S, len_i^2)\) ,且 \(\sum_{i=1}^n len_i = S\) 。根据均值不等式能够获得 \(n\) 和 \(len_i\) 都取 \(\sqrt{2S}\) 时达到上界,总复杂度 \(O(S\sqrt{2S})\) 。class
然而事实上后面那个下界不容易卡满,由于你须要构造一个字符串其每个前缀都能在 \(\text{parent}\) 树上分支出最长的一条链,因此这个根号实际上跑起来比某些大常数的一个 \(log\) 作法还快。效率
BZOJ3277 字符串遍历
题意:给出 \(n\) 个字符串,对于每个字符串求出其有多少个子串在至少 \(k\) 个字符串中出现过。统计
能够说是这个特技的模板题了,不过线段树合并能够作 \(O(nlogn)\) ,这里就只说根号作法吧。建出后缀自动机后枚举每个串的叶子节点暴力往上跳给祖先节点出如今不一样的母串的次数 \(+1\) 便可,已经被别的该串节点遍历过就跳出,复杂度就是上述的 \(O(S\sqrt{2S})\) 。数据
NOI2018 你的名字
题意:对于每个询问串,求出其有多少个不一样子串在母串的 \([L, R]\) 之间出现过。
将问题转化为求有多少个子串在询问串和母串的 \([L, R]\) 之间共同出现便可,暴力枚举的同时额外加一个线段树合并判是否在 \([L, R]\) 便可,复杂度 \(O(S\sqrt{2S}logS)\) 。不过套上 \(log\) 之后会由于第二个上界被卡高后被叉掉,可是 \(68pts\) 不须要线段树合并判断,能够确保经过。实际上3s内跑过了全部测试点
Sum of Squares of the Occurrence Counts 增强版
没增强以前这个特技不能作
题意:给出 \(n\) 个串,对于每个串 \(i\) 求出其全部子串在串 \([1,i]\) 之间的出现次数的平方和。
对于全部串建后缀自动机,用线段树合并维护出 \(\text{parent}\) 树上每个节点拥有来自哪几种母串的 \(\text{endpos}\) ,以及每一种母串对应的 \(\text{endpos}\) 数量。考虑每个母串对全部线段树大小之和的贡献就是叶子的并,因而暴力遍历合并后的每一棵线段树的总复杂度就是 \(O(S\sqrt{2S})\) 。直接在线段树上暴力统计每个 \(\text{parent}\) 树上节点对每个串的答案的贡献便可,总复杂度 \(O(S\sqrt{2S}+SlogS)\)。
若是你想知道会了这个根号特技有什么用,我也说不清。对于我这种字符串菜鸡来讲,写简单好写的作法比套上各类数据结构好调多了,这个特技牺牲了一些时间效率,可是大大简化了思惟难度和代码难度。固然若是您是神仙彻底能够去秒正解。