ABC280Ex Substring Sort

我永远喜欢数据结构。

看不懂官方题解,来一个 \(2\log\) 垃圾做法。你敢信垃圾做法还要去询问 UOJ 群和 LA 群巨?

洛谷 AT

  • 给出 \(n\) 个字符串 \(s_1\sim s_n\)。记 \(F(i,l,r)\) 表示 \(s_i[l,r]\) 这个子串。将所有存在的 \(F(i,l,r)\) 非降排序。\(q\) 次询问,求出排在第 \(k\) 位的 \(F(i,l,r)\)。如有多解输出任意一个。

  • \(n,\sum\limits_{i=1}^n|s_i|\le 10^5\)\(q\le 2\times 10^5\)\(k\le \sum\limits_{i=1}^n \dfrac{|s_i|(|s_i|+1)}{2}\)

  • \(\text{2 s / 1 GB}\)

先将所有字符串拼成大串 \(S\)。其中 \(\mathcal{O}(|S|)=\mathcal{O}\left(n+\sum\limits_{i=1}^n|s_i|\right)\)。对 \(S\) 进行后缀排序。

称一个串在排名为 \(i\) 的后缀中第一次作为前缀出现,当且仅当:不存在 \(j<i\),使得这个串在排名为 \(j\) 的后缀中作为前缀出现。

对于 \(S\) 中排名为 \(i\) 的后缀,预处理 \(sum_i\) 表示有多少种本质不同的原串中的子串在这个后缀中第一次作为前缀出现;预处理 \(tot_i\) 表示有多少个原串中的子串在这个后缀中作为前缀出现。然后分别对这两个数组求前缀和,记为 \(ssum_i\)\(stot_i\)(代码中用的是同一个数组)。

考虑求出答案是第几大的本质不同子串。容易发现排在第 \(k\) 位等价于,它是最大的一种子串,使得小于它的子串数量小于 \(k\)。这个有单调性,可以二分利用 \(ssum_i\) 判断求。

记二分的子串为 \(str\)。考虑对于每个后缀统计小于 \(str\) 的子串数量。先找到它第一次出现在哪个排名的后缀中,设这个排名为 \(p\)。分为 \([1,p)\)\([p,|S|]\) 两部分。

对于第一部分,这些后缀的所有前缀都小于 \(str\)。因为若某个前缀 \(pre\)\(str\) 前缀,其必然是真前缀。不然 \(p\) 就不是 \(str\) 第一次出现的位置。那么 \(pre\) 字典序小于 \(str\)。否则,它们跨过了 \(\text{LCP}\),又因为 \([1,p)\) 的后缀排名更小,所以 \(pre\)\(\text{LCP}\) 后的那个位置上一定小于 \(str\),因此 \(pre\) 字典序小于 \(str\)

对于第二部分,答案为 \(\sum\limits_{i=p}^{|S|}\min\{|\text{LCP}(S[i,|S|],S[p,|S|])|,|str|-1\}\)。同样考虑为 \(pre\)\(str\) 真前缀以及跨过 \(\text{LCP}\) 的情况。

第一部分答案即为 \(stot_{p-1}\)。第二部分考虑将 \(\min\) 拆开。由于 \(|\text{LCP}(S[i,|S|],S[p,|S|])|=\min\limits_{j=p+1}^i \text{height}_j\),这个式子的值是单调不升的。可以二分出一个分界点 \(pos\) 使得当 \(i\in[p,pos]\)\(\min\{|\text{LCP}(S[i,|S|],S[p,|S|])|,|str|-1\}=|str|-1\);当 \(i\in(pos,|S|)\)\(\min\{|\text{LCP}(S[i,|S|],S[p,|S|])|,|str|-1\}=\min\limits_{j=p+1}^i \text{height}_j\)。显然有 \(pos\ge p\),因为 \(p\) 这个后缀以 \(str\) 为前缀。

那么第一种情况的贡献就是 \((pos-p+1)(|str|-1)\);至于后一种情况,根据 \(\min\) 的结合律,可以将其改写成 \(\sum\limits_{i=pos+1}^{|S|}\min\limits_{j=pos+1}^{i}\text{height}_j\)。记为 \(f_x=\sum\limits_{i=x}^{|S|}\min\limits_{j=x}^{i}\text{height}_j\)。由于没有修改,考虑预处理。

可以用单调栈,对于每个 \(x\) 求出最小的 \(y\),满足 \(y>x\)\(\text{height}_y<\text{height}_x\)。记这个 \(y\)\(nxt_x\)。同样将 \(f_x\) 分为 \(i\in[x,nxt_x)\)\(i\in[nxt_x,|S|]\) 两部分。对于第一部分,根据 \(nxt_x\) 的定义可知这些 \([x,i]\) 的最小值均为 \(\text{height}_x\),贡献为 \((nxt_x-x)\text{height}_x\);对于第二部分,再运用 \(\min\) 的结合律写成 \(\sum\limits_{i=nxt_x}^{|S|}\min\limits_{j=nxt_x}^i \text{height}_j\),你会发现它就是 \(f_{nxt_x}\)。于是我们得到了 \(f_x\) 的求法:

\[f_x=(nxt_x-x)\text{height}_x+f_{nxt_x} \]

\(f_x\) 求出来之后,就可以将 \(\sum\limits_{i=pos+1}^{|S|}\min\limits_{j=pos+1}^{i}\text{height}_j\)\(f_{pos+1}\) 带入求解了。

这样一来我们就找到有多少个子串小于给定的串 \(str\)。结合第一次二分就可以得到答案是哪一种本质不同的子串。为了方便,这一步在返回小于 \(str\) 的串个数时,同时返回 \(str\) 第一次出现后缀属于原串中的第几个串的哪个位置,便于得到 \(F(i,l,r)\)。这些都是容易求的,可以考虑记录 \(S\) 中每个排名后缀属于哪个原串、原串在 \(S\) 中出现的位置。

那么这题就做完了,时间复杂度为 \(\mathcal{O}((q+|S|)\log^2|S|)\),空间复杂度为 \(\mathcal{O}(|S|\log |S|)\)

AC Code

posted @ 2024-02-15 14:35  lzyqwq  阅读(30)  评论(0)    收藏  举报