题解:P4482 [BJWC2018] Border 的四种求法
lalaouye
·
·
题解
这里较严谨地证明了 Border 理论并给出了较为详细的题目解法。
令 pre(i,s) 表示 s_{[1,i]},suf(i,s) 表示 s_{[i,|s|]}。
证明:不妨令 $q\ge p$,$d=q-p$。
对于 $1\le i\le |S|-q$,$s_i=s_{i+q}=s_{i+q-p}=s_{i+d}$。
对于 $p+1\le i\le|S|$,$s_i=s_{i-p}=s_{i-p+q}=s_{i+d}$。
由于 $p+q\le |S|$,所以 $p\le|S|-q$,那么对于所有的 $i$ 都有至少一个限制可以满足。且 $d=q-p$ 是个辗转相减的形式,也就是 $\gcd$。
$\mathbf{Periodicity\ Lemma}$:若 $p,q$ 是字符串的两个周期,且满足 $p+q-\gcd(p,q)\le|S|$,那么 $\gcd(p,q)$ 也是 $S$ 的一个周期。
这个定理网上貌似很少有人证明,不知道是不是因为太简单了,我还是证一下吧。考虑对于 $i\in(|S|-q,p+1)$ 怎么保证 $s_i=s_{i+\gcd(p,q)}$。
尝试使用归纳法,首先如果 $p|q$ 显然成立。
而对于一般情况,当 $\gcd(p,q)$ 是 $suf(p+1,S)$ 的一个周期,我们便可以证明 $s_i=s_{i+\gcd(p,q)}$,同样也能够证明 $s_i=s_{i+d}$。如果满足 $p+q-\gcd(p,q)\le|S|$,对于 $suf(p+1,S)$ 的两个周期 $p,q-p$ 来说,容易证明 $p+(q-p)-\gcd(p,q-p)\le|S|-p$,容易发现两式等价。通过不断归纳可以归纳到 $p|q$ 的情况。
介绍完周期定理后就可以介绍其它一些常用引理了。
**引理**:若字符串 $s$ 和 $t$ 满足 $|s|\le |t|\le 2|s|$,则 $s$ 在 $t$ 中的所有匹配位置构成了一个等差数列,且公差为最小周期。
证明:只需要考虑匹配次数大于 $2$ 的情况。在这种情况下,显然匹配的区间之间是有交的。容易证明不存在两个位置相邻的匹配之间差值不为最小周期,手玩一下即可。
**引理**:对于字符串 $S$,所有大于等于 $\frac {|S|}{2}$ 的 border 的长度构成等差数列。
证明:显然一个长为 $p$ 的 border 对应一个长为 $|S|-p$ 的周期。那么我们考虑把最长的 border 找出来,设其长为 $p$,可知存在周期为 $|S|-p$。对于其它的满足条件的 border,若长为 $q$,那么显然 $|S|-p$ 和 $|S|-q$ 这对周期满足周期引理的条件,所以 $\gcd(|S|-p,|S|-q)$ 同样是 $|S|$ 的周期,而结果只能为 $|S|-p$,否则与 $p$ 为最长的 border 相矛盾。所以对于任意满足条件的 $q$ 都有 $|S|-q$ 整除 $|S|-p$。而所有长为 $|S|-p$ 倍数的周期都对应了一个 border,得证。
现在我们类似倍增分块,将 border 按长度分为 $[1,2),[2,4),\cdots,[2^k,|S|)$ 这些集合。
**引理**:每个集合内 border 的长度构成等差数列。
证明:找出最长的 border,集合内其它 border 肯定也是最长 border 的 border,且长度都满足上一个引理的条件。
这意味着,我们可以将一个字符串的 border 分成 $\log n$ 个等差数列表示,我们可以利用它来干一些很厉害的事情,比如说 [[BJWC2018] Border 的四种求法](https://www.luogu.com.cn/problem/P4482)。
怎么找一个子串的 $\log n$ 个等差数列呢?现在尝试对于每个集合找出它对应的等差数列,由于最后一个集合上界是 $|S|$,有点特殊,我们先想其它集合怎么处理。这里有个很巧妙的方法,对于 $[2^k,2^{k+1})$ 这个集合,我们让 $pre(2^k,S)$ 放到 $suf(2^{k+1}-1,S)$ 匹配,$suf(2^k,S)$ 放到 $pre(2^{k+1},S)$ 匹配。那么将匹配位置平移后求交就是 border 序列。根据引理,我们发现这两次匹配的匹配位置同样构成等差数列,我们只需要找到前两次匹配位置和最后一次匹配位置就能完美刻画等差数列了。
直接 KMP 显然在多组询问中不优,我们考虑哈希。由于我们知匹配长度为 $2$ 的整次幂的串,我们可以 $\mathcal{O}(n\log n)$ 预处理每个串的哈希值。现在我们要处理形如一个二元组 $(s,i)$ 的询问,表示查询在 $i$ 的左边或右边 $s$ 第一次匹配的位置。但是对于 $|s|=2^k$ 的查询,对于 $i$ 我们只关注 $[i,i+2^k]$ 这个区间中有没有答案,想想我们在什么情况下才查询的。这样我们对于序列每 $2^k$ 个位置就分一个段,总段数显然是 $\mathcal{O}(n)$ 的,每个段存段内每个哈希值的等差数列,然后我们查询位置的时候只需要涉及到两个段就可以查出 $pre$ 和 $suf$ 匹配时的等差数列了。
接下来等差数列合并貌似是个棘手的问题,直接做的话只能写非常麻烦的双 $\log$ 做法。如果想做到简洁的单 $\log$ 需要继续分析性质。
**引理**:四个字符串满足 $|x_1|=|y_1|\ge|x_2|=|y_2|$,且 $x_1$ 至少在 $y_2y_1$ 中匹配了 $3$ 次,则 $per(x_1)=per(y_1)$。
证明:考虑使用反证法,假设 $per(x_1)>per(y_1)$,考虑 $x_1$ 在 $y_2y_1$ 最右边一次匹配,令 $z$ 表示 $x_1$ 与 $y_1$ 重叠的部分,那么 $|z|\ge 2per(x_1)>per(x_1)+per(y_1)$,并且 $per(x_1)$ 和 $per(y_1)$ 都是 $z$ 的周期,那么 $\gcd(per(x_1),per(y_1))$ 也是 $z$ 的周期,但是它显然是要小于 $per(x_1)$ 的,与 $per(x_1)$ 是最小周期矛盾。
这意味着,两个等差数列要么公差相同,要么长度不超过 $3$。这样就可以 $\mathcal{O}(1)$ 合并等差数列了。
总复杂度 $\mathcal{O}(n\log n)$,常数较大,[代码](https://www.luogu.com.cn/paste/zooexg4j)。