字符串问题选讲
[国家集训队] 最长双回文串
Manacher板子题,先跑出每个点为中心的最长回文串,然后求出每个点为左右端点的最长回文串,之后枚举分界点统计答案即可。
「JZOI-1」拜神
题目中所求为至少出现两次最长的子串,转化得到就是求区间内两个后缀最长的LCP。
考虑建立SAM,然后在parent树上启发式合并endpos集合。可以发现在合并两个集合,即求出这两个集合之间两两LCP的时候,只有最近的两个是有用的。因此可以用set维护整个过程,然后找到这 \(O(n\log n)\) 个区间之后再用线段树查询即可。
[BOI2004] REPEATS
我的做法同上一题,直接启发式合并找到有用的 \(O(n\log n)\) 个即可。
但是实际上有更巧妙的做法,先枚举长度,然后洒关键点,最后和优秀的拆分差不多做即可。
Check,Check,Check one two!
说实话是比较巧妙的一道题。
你发现这个东西又有LCP又有LCS不是很好维护,可以考虑搞出正反串的parent树然后点分啥的但是没必要。我们发现 \([i,i+lcp(i,j)-1]\) 和 \([j,j+lcp(i,j)-1]\) 是相等的,而 \([i-lcs(i,j)+1,i]\) 和 \([j-lcs(i,j)+1,j]\) 也是相等的,也就是说 \([i-lcs(i,j)+1,i+lcp(i,j)-1]\) 和 \([j-lcs(i,j)+1,j+lcp(i,j)-1]\) 是相等的。因此我们可以枚举 \(l,r\) 表示上面两个区间的左端点,那么要求 \(s_{l-1}\not=s_{r-1}\) 然后贡献为 \(f(LCP(l,r))\),其中\(f(x)=\sum\limits_{i=1}^{n-1}{i(n-i+1)[i\leq k1][n-i+1\leq k2]}\),分讨可以 \(O(1)\) 算出。
求 \(LCP(l,r)\) 只需要在parent树上合并即可。时间复杂度 \(O(n|\sum|)\)
[JRKSJ R4] Salieri
垃圾题,赛时就想骂人。
显然对模式串建立AC自动机,然后每个文本串在AC自动机上跑匹配,每个点给到根路径上模式串的结尾点加 \(1\) 表示出现次数。
最这些点建立虚树,可以发现虚树上每个点和父亲之间的出现次数是一样的。\(k\) 大值可以考虑二分答案,然后变成查询树上一条直上直下的链中大于某个值有多少个,这个直接无脑主席树就好了。
时间复杂度 \(O(\sum|S|\log ans\log w)\),5e5放两个log也不知道出题人怎么想的。
Everybody Lost Somebody
比较牛逼的题目。
首先来考虑 \(height\) 给全了以后的限制,显然是要求往后 \(height_i\) 一段相等,然后后面一位不等,可以用并查集/差分来维护。
现在考虑两个之间 \(height\) 是不定的情况,无非两种:前一个比后一个小,或者前一个和后一个一样而且前一个在原串的后面一位小于后一个在原串的后面一位。第一种对应于首字母变化,第二种无需去管。
同时我们发现只要最小化 \(s_{sa_i}\) 的字典序也就最小化了原串的字典序,因此可以贪心让每个首字母取到最大的能取到的位置即可,时间复杂度可以做到\(O(n)\)。
CF1043G Speckled Band
显然只要有两个一样的字母就是有解的,那么可以先判掉无解的情况,之后发现答案上界为\(4\),也就是我们分成最多 \(5\) 段,其中两个一样的字母单独一段,然后答案就是\(4\)。于是变成了一个个判答案是否可行的问题。
因为可以把字符串反过来做一遍所以不考虑对称的情况。
如果这个串是整周期的字符串,那么答案可以为 \(1\) ,只要拆若干个周期出来即可。这个可以尝试将长度除以某个质因数,检验LCP即可。
然后考虑答案为\(2\)的情况有两种:要么原串有border,要么开头有两个一样的串。
答案为 \(3\) 的情况大概是:串中存在两个开头的字母,或者中间有两个一样的串按挨在一起。
判完这些以后就是答案为 \(4\) 的情况。
现在我们来解决怎么判断这些问题。
可以用类似优秀的拆分的做法来算出以每个点开头,最小的长度使得这个长度的串重复了两次。如果这两个串在原串中间可以处理合法左端点的前缀max。
判断一个区间是否有border可以用border的四种求法降维打击,但是没必要,有一种优美的根号分治做法。
具体的,如果某个border在原串中有重叠,那么一定存在更小的border,因此我们只考虑最小的border,它们在原串中一定不重叠。则如果这个border小于 \(\sqrt n\) ,直接暴力判断,如果大于 \(\sqrt n\) ,则在SA的rk上向两边查border大于 \(\sqrt n\) 的,根据上面的结论只会查 \(\sqrt n\) 个。
时间复杂度\(O(n\log n+q\sqrt n)\)
[BJWC2018]Border 的四种求法
题解区关于重链上的做法貌似做烦了?
我们先把这个border用有关后缀的东西表示出来,不难发现对于给定区间 \([l,r]\) ,我们要求 \(\max j\) 使得 \(LCP(j,r)+j\geq r+1\),且 \(j\in(l,r]\)。
考虑将其放到parent树上,则LCP 相当于两个点在树上的深度,特别的,如果为祖先后代关系那么是祖先在原串后缀的长度。
深度问题考虑树剖,那么我们将每个后缀转化成 \(O(\log n)\) 次跳轻边的过程,这样的好处是每次跳轻边都可以确定出 LCA,也即深度。同理询问也可以这么拆。那么接下来我们只需要考虑一条重链上的问题。
我们记每个后缀的二元组为 \((j,lcp_j)\),询问的三元组为 \((l,r,dep_i)\) 则如果这个后缀能被统计入答案,需要满足:\(j\in(l,r],\min(lcp_j,dep_i)+j\geq r+1\)。
把 \(\min(lcp_j,dep_j)\) 拆了,得到 \(lcp_j+j\geq r+1\),且 \(dep_i+j\geq r+1\)。我们发现实际上将上面三个式子整合以后是两维的问题,也即:\(j\in [\max(l+1,r+1-dep_j),r]\),且 \(lcp_j+j\geq r+1\)。
这个是平凡的,将后缀按照 \(lcp_j+j\),询问按照 \(r+1\) 排序后双指针,然后用个树状数组维护大于某个数的最小值即可,如果你不想写你也可以写set,反正跑得飞快。总时间复杂度 \(O(n\log^2 n)\)。