zoukankan      html  css  js  c++  java
  • 从《逆にする関数》(《逆转函数》)看Manacher算法的扩展应用

    简述Manacher算法

    Manacher算法用于在 $O(|s|)$ 的时空内预处理一个字符串 $s$ 的任意子串是否为回文串。方法大致如下:

    回文串必有回文中心,它可能是一个字符,也可能是两个字符中间的空隙。令 $s[l, r]$ 的中心标号为 $l+r$, 我们只需处理每个中心 $i$ 对应的最大回文串长度 $r_i$, 就能在 $O(1)$ 内判断一个子串是否回文串。

    朴素算法:一开始令 $r_i=i mod 2+1$, 反复比较当前回文串左右两侧的字符来扩展。

    优化:对于 $j<i$ 中最大的 $j+r_j$, 即目前已知最右回文串的右端点,若 $i le j+r_j$, 则我们知道回文中心 $i$ 与回文中心 $2j-i$ 关于回文中心 $j$ 对称。由对称性可取 $r_i$ 的初值为 $min{r_{2j-i}, j+r_j-i}$. 这一过程在下面的叙述中将叫做“继承”。

    时间复杂度分析:若继承后 $i+r_i<j+r_j$, 则由对称性知不可能继续扩展;否则每扩展一次,$j+r_j$ 将增大 $1$, 而 $j+r_j le 2|s|$, 从而该算法时间复杂度 $O(|s|)$.

    实现上通常都是将原串的任意两个字符之间插入空字符,但是在下面的例题中我没有采用这种写法。

    Manacher算法的扩展应用

    下面的例题将说明:

    • 如何在类回文问题上使用Manacher, 或者说,Manacher只使用了回文串的哪些性质;
    • 如何在Manacher过程中维护(类)回文区间权值和。
    • 在Manacher的“继承”过程中逐格缩短的技巧,及其复杂度正确性。

    例题:[nikkei2019-2-final H]逆にする関数

    题意

    记 $A=[1, m] cap mathbb Z$. 对于由 $A$ 的元素构成的数列 $(v_1, v_2, ldots, v_k)$ 以及函数 $f: A o A$, 称“$f$ 逆转 $v$”或“$f$ 是 $v$ 的逆转函数”,当且仅当 $forall 1 le i le k$, $f(v_i)=v_{k+1-i}$.

    对于由 $A$ 的元素构成的数列 $(a_1, a_2, ldots, a_n)$, 求其所有连续子列的逆转函数个数之和模 $998\,244\,353$.

    保证 $n, m le 3 imes 10^5$.

    题解

    首先观察两个性质:

    • 若 $f$ 逆转 $A$, 且 $|A| ge 3$, 那么函数 $f$ 逆转 $A[1:-1]$.
    • 若 $f$ 逆转 $A$, $g$ 逆转 $A[:L]$, 那么函数 $f circ g circ f$ 逆转序列 $A[-L:]$.

    以上两点正说明了,我们可以使用Manacher算法来维护:对于每个对称中心,最长的关于它对称的存在逆转函数的区间。我们设关于 $i$ 对称的存在逆转函数的区间最大长度为 $r_i$.

    函数 $f$ 逆转区间 $[l, r]$ 当且仅当:$forall 0 le x le r-l$, $f(a_{l+x})=a_{r-x}$, 这也意味着 $f(a_{r-x})=a_{l+x}$, 所以实际上这是以 $[l, r]$ 的对称中心为中心进行匹配。

    存在逆转函数当且仅当这些匹配关系不冲突。若逆转函数存在,则其个数为 $m^{m-n(l, r)}$, 其中 $n(l, r)$ 表示区间 $[l, r]$ 内不同的数的种类数。

    为了维护是否存在逆转函数,及逆转函数的个数,可先对于每个位置 $i$, 求 $a_i$ 的上一次出现位置 $p_i$(若不存在记为 $0$)与下一次出现位置 $n_i$(若不存在记为 $n+1$)。

    考虑怎么判断区间 $[l, r]$ 是否存在逆转函数。由于Manacher算法的特性,可以这么判断:

    $a[l, r]$ 存在逆转函数,当且仅当 $r-l le 1$, 或下列三个条件都成立:

    • $a[l+1, r-1]$ 存在逆转函数;
    • $n_l ge r$ 或 $a_{l+r-n_l}=a_r$;
    • $p_r le l$ 或 $a_{l+r-p_r}=a_l$.

    考虑 $n(l, r)$ 怎么求。由于Manacher算法的特性,可以这么计算:$n(l, r)=egin{cases}1, & l=r; \ 1+[p_l e p_r], & r-l=1; \ n(l+1, r-1)+[n_l>r]+[p_r le l], & r-l>1.end{cases}$

    由此,从 $n(l, r)$ 也可以反推回 $n(l+1, r-1)$.

    现在我们还需要维护 $sum_{x=0}^{leftlfloorfrac{r-l}{2} ight floor}m^{m-n(l+x, r-x)}$.

    在Manacher的“扩展”过程中,这是很好维护的。但是在“继承”过程中,回文半径从中心的对称点继承过来的时候可能要缩短,这造成了麻烦。

    我们考虑这么做。在中心的对称点处,每次两端各去掉一个位置,把它收缩到合适的长度后再翻折到目前的中心。在这里,如果“目前最右回文串”有多个时,必须取最短的那个。

    假设最右回文串中心为 $c$, 现在欲求解 $r_{c+k}$, 则我们知道 $r_{c-k}+c-k le r_c+c$, 即 $r_{c-k}-r_c le k$. 所以本次收缩次数为 $(c-r_c)-(c-k-r_{c-k})=r_{c-k}-r_c+k le 2k$. 收缩后,$c+k$ 将接替 $c$ 成为新的最右回文中心,因此 $k$ 又表示最右回文中心右移的距离,其总和不超过 $n$. 所以,总收缩次数为 $O(n)$. 每次扩展都会令最右回文串的右端点右移,总扩展次数为 $O(n)$. 因此这个看似是暴力的算法就是 $O(n)$ 复杂度的。

    代码链接

  • 相关阅读:
    [一起面试AI]NO.9 如何判断函数凸或非凸
    [一起面试AI]NO.8 在机器学习中,常用的损失函数有哪些?
    [一起面试AI]NO.5过拟合、欠拟合与正则化是什么?
    [一起面试AI]NO.4特征工程主要包括什么?
    [一起面试AI]NO.3分类问题常用的性能度量指标有哪些
    MySQL中自增ID修改起始值
    折半查找算法(Python版)
    彻底解决安卓7.0及以上版本抓包https失败
    Charles抓包2-Charles抓取https请求
    Charles抓包1-Charles安装汉化(附正版注册码)
  • 原文地址:https://www.cnblogs.com/nealchen/p/nikkei2019-2-final_H.html
Copyright © 2011-2022 走看看