P4770 [NOI2018] 你的名字 做题记录

发布时间 2023-12-01 20:34:35作者: 蒟蒻·廖子阳

我永远喜欢数据结构

题目传送门

  • 给出字符串 \(s\) 以及 \(q\) 个询问,第 \(i\) 个询问给出一个串 \(t_i\) 以及一个区间 \([l_i,r_i]\)

  • \(s[l,r]\) 为字符串 \(s\)\(l\) 位到第 \(r\) 位字符顺次拼接而成的子串。形式化地,\(s[l,r]=\overline{s_ls_{l+1}\dots s_r}\)

  • 对于每个询问,求 \(t_i\) 有多少种本质不同的子串没有在 \(s[l_i,r_i]\) 中出现。

  • \(|s|\le 5\times 10^5,q\le 10^5,\sum\limits_{i=1}^q|t_i|\le 10^6\)

  • \(\text{5.00 s / 768.00 MB}\)

神仙字符串题。

首先把所有字符串用特殊字符接起来,得到一个大串 \(S\)。对 \(S\) 进行后缀排序。这样不改变任意两个后缀的 \(\text{LCP}\)

对于每一组询问,考虑容斥,即用 \(t_i\) 中的本质不同子串个数减去在 \(s[l_i,r_i]\) 中出现过的。

前半部分是平凡的,即按排名考虑每一个后缀带来的本质不同子串个数,根据经典结论就是这个后缀的前缀数减去它的 \(\text{height}\)

至于后半部分,同样这样考虑每个后缀带来的本质不同子串中有多少个在 \(s[l_i,r_i]\) 中出现。我们发现若一个后缀 \(\boldsymbol{t_i[j,e]}\)\(\boldsymbol{s[l_i,r_i]}\) 中出现,则 \(\boldsymbol{t_i[j,e-1]}\) 也在 \(\boldsymbol{s[l_i,r_i]}\) 中出现。所以可以考虑二分这个最大的结束位置 \(e\)。判断 \(t_i[j,e]\) 是否在 \(s[l_i,r_i]\) 中出现就是判断是否存在一个位置 \(k\) 使得 \(k\in[l_i,r_i-e+j]\)\(|\text{LCP}(S[k,|S|],t_i[j,e])|\ge e-j+1\)

二分出排名区间,主席树二维数点即可。但是这样回答单组询问的时间复杂度为 \(\mathcal{O}(|t_i| \log |S|\log |t_i|)\),无法接受。

思考一下二分的目的,我们想要对于 \(t_i\) 的每个后缀,得到一个最大的长度 \(L_j\),使得 \(t_i[j,j+L_j-1]\)\(s[l_i,r_i]\) 中出现。

我们发现一个关键性质,那就是 \(\boldsymbol{L_j\ge L_{j-1}-1}\)。因为这两个后缀只差了开头的这一位。

我们可以类似于 \(\text{height}\) 数组那样,用一个指针 \(k\) 表示当前 \(t_i[j,j+k-1]\)\(s[l_i,r_i]\) 中出现,检查是否可行时仍然二分排名区间 + 主席树。若可行则 \(k\) 右移。

由于最多递减 \(\mathcal{O}(|t_i|)\),因此 \(k\) 最多移动 \(\mathcal{O}(|t_i|)\) 次,这样单组询问的时间复杂度就是 \(\mathcal{O}(|t_i|\log |S|)\)

综上,我们得到了一个时间、空间复杂度均为 \(\mathcal{O}(|S|\log |S|)\) 的做法。

提交记录(\(\color{limegreen}\bf Accepted\space100\)\(\bf{4.62\, s / 606.29\, MB}\) 代码