省选集训-字符串杂题
基础子串结构太ex就不看了
CF1827C
考虑如何将一个由若干偶回文串拼接而成的串分解开。
容易发现每次从后往前删掉最短回文串就是对的。
所以问题变成求 \(l_i\) 表示 \([l_i,i]\) 是以 \(i\) 为结尾的最短回文串位置。
则可以 \(dp\) 计算,\(dp_i=dp_{l_i-1}+1\),\(ans=\sum dp_i\)
\(l\) 可以使用 manacher 然后做区间染色得到。
CF1913F
屑题。
考虑计算出 \(c_x\) 表示改变 \(a_x\) 后消失的回文串数量,\(w_{i,x}\) 表示将 \(a_i\) 改为 \(x\) 后新增的回文串数量即可计算出答案。
考虑使用 manacher 求出原本的各个回文中心以及回文半径,将其往外扩张一个单位后(这个位置失配)再使用哈希求出最长长度,就可以对 \(w\) 进行计算了。
以及 \(c\) 的计算事实上根据回文半径可以变成区间加等差数列。
考虑求出 \(c\) 的二阶差分,然后计算即可。
区间加等差数列别推错了,就尴尬了。
P10716
注意到 \(A\) 是该前缀的 border,并且 \(B\) 可以为空。
那么找到最长的 \(border\) 后,这个串的 \(border\) 个数即为所求。
考虑失配树(\(nxt\) 树),也即 \((i,k)\) 的询问是找到 \(i\) 合法的最近祖先。
可以倍增,问题变为如何判断。
注意到调和级数求和,因此对于每个前缀 \([1,i]\),都预处理出 \(k=1\sim \lfloor\frac{n}{i}\rfloor\) 的最短前缀长度(可能没这么多次)。
那么就只剩下一个判断了。
找的话,\(pos\) 后的下一次出现位置可以变成子树内大于某个值的最小编号寻找,失配树上线段树合并即可。
P11150
关于讲课ppt是错的这件事。
要最小化原串的字典序,那么首先就要找到改后第一个与原串不同的位置 \(np\),让这个位置尽量小。
可以对原串建立一个SAM,然后将 \(B\) 在 SAM 上逐位匹配(相当于枚举 \(np\) 这个位置由 \(B\) 的哪一个位置做出贡献)
那么首先就要求了 \(np\) 最小。在 \(np\) 相同时,我们再哈希判断两个不同的 \(B\) 接入位置谁更优。
这样我们就找到了一个最优的接入位置 \(r\),考虑将其变为最小的接入位置。
稍加画图可以发现,对于效果相同的接入位置,那么一定是原串匹配上了 \(B\) 的循环节。
所以倍增往前跳的循环节个数,然后哈希求出新的方案里得到的新串哈希值是否与原本串相同即可。
CF1975G
首先做一些讨论:
- \(s,t\) 不含 \(*\),可以直接判断
- \(s,t\) 都含 \(*\),将第一个 \(*\) 前面的以及最后一个 \(*\) 后面的位置进行匹配判断,判断通过显然有解。
- \(s,t\) 仅有一个含 \(*\),类似 \(2.\) 先去掉第一个 \(*\) 以及最后一个 \(*\) 前/后面的串。
那么一般地,可以看作 \(s=*s_1*s_2*\dots *s_m*\) 与 \(t\) 不含 \(*\) 进行匹配。
贪心地,我们动态维护 \(p\),逐个将 \(s_i\) 与 \(t[p,n]\) 进行匹配,并将 \(p\) 置为第一个匹配到的位置之后(也就是逐个匹配)
考虑到单次匹配代价太大,需要将单次匹配复杂度降至 \(|s_i|\) 级别。
很厉害的地方是将 \(t[p,p+2|s|)\) 拿出来匹配,不管有没有匹配位置,\(p\) 至少增加 \(|s|\)
然后就考虑 \(|s|\) 级别复杂度匹配,关于带通配符的字符串匹配,可以考虑 NTT,也就是将每个字符赋权 \(w_i\),通配符为 \(0\),计算:
这可以拆为三次差卷积计算。
P11291
注意到 \(R\) 可以为空,所以固定 \((l,r)\) 后 \(k\) 是一段区间,固定 \((l,k)\) 后 \(r\) 也是一段区间。
但是性质更好的是固定 \((l,k)\),不妨设 \(f_{l,k}\) 为其对应的最大 \(r\),设 \(r_i\) 为最大的满足 \([i,r_i]\) 是好串的值。
则显然 \(f_{l,k}=\max_{i\le j\le f_{l,k-1}}(r_j)=\max_{i\le j\le r_i}f_{j+1,k-1},f_{l,1}=r_l\)
但是注意到最远的,那么一定 \(r\) 所有的都不为空,那么对于最右侧的形式,以 \(k=3\) 为例,有:
\(f_{l,3}=\max_{i\le j\le r_i+1}f_{j,2}=\max_{i\le j\le r_i}\max_{j\le k\le r_{j+1}}r_{k}\)
而注意到对于跳跃方案:\(l\to j\to k\to \dots\) 每一步都应当是最优化的。
形式地说,设 \(to_i\) 为 \(\max_{i\le j\le r_i+1} r_j\) 取到最优的 \(j\),\(f_{l,2}\) 转移时用了 \(to_l\),\(f_{l,3}\) 转移时在 \([l,f_{l,2}+1]\) 里找一个 \(to\) 最大的转移,而这个位置显然不会低于 \(to_l\)(\(f\) 单调不降),因此就变成了 \(to_l\) 的 \(f_{2}\) 转移,也就是找到的位置是 \(to_{to_l}\)。
因此就变成了跳 \(to\) 的问题,建 \(to\) 树,就变成了树上统计的问题。
对于问题一是好做的,相当于只需要 dfs 时维护一个链和,而问题二也可以变成若干区间加等差数列,可以二阶差分解决。
CF1975H
考虑到如果我们要让最大子串最小,那么设 \(z\) 是最大字符,重排后一定形如 \(z+s_1+z+s_2+\dots +s_m+z\) 的形式。
且最大子串一定是一个后缀,这是显然的。
因此这相当于是确定下了后缀的最后面是一个 \(z\),然后我们继续将 \(z+s_i\) 视作一个字符,变成了子问题。
所以当最大字符数小于等于其余字符数的时候,我们找到了一个让问题规模减半的方法。
但 \(s_i\) 应当如何划分?注意到按目前顺序,也自然有 \(s\) 的字典序不降的说法,且每个 \(s\) 的第一个字符应当组成原本其余字符排序后的一个前缀。
事实上,考虑这样一个贪心:
维护 \(i,p\),初始化为 都为 \(1\),设其余字符排序后是 \(a\),那么我们每次将 \([p,m]\) 的 \(s\) 按顺序加上 $a_i,a_{i+1},a_{i+2}\dots $,这样填完了一轮之后再将 \(p\) 变成当前末尾最大的 \(s\) 的首次出现为止(也即 \(s_{p-1}<s_p=s_{p+1}=\dots =s_m\))
这样做显然是对的,我们保证了前面尽可能小,且后续越往后越大,且“变大量”更小。
那么在最大字符数大于其余字符数后怎么办?事实上可以采用同样的处理办法,但是我们要预处理处理结果(保证复杂度)
这个处理结果相当于每个其余字符前面多了相同数量且最多个 \(z\),同时还空余出了若干个 \(z\) 作为余下的最大字符,然后末尾也都出了这么多个 \(z\)。
保证每次递归问题规模减半,问题解决。
CF2053G
考虑单次操作的过程,不妨设长串为 \(s_1\),短串为 \(s_0\)。
若 \(s_0,s_1\) 都可以表达为一个串 \(t\) 重复至少一次得到,则可以通过 KMP 求出原串的最小循环节直接判断。
那么就只需要处理 \(s_0,s_1\) 无共同循环节的情况了。
不妨设 \(s_1=s_0^k+c\),且 \(k\) 最大,也即 \(s_0\) 不是 \(c\) 的前缀,我们先不断贪心匹配短串,当短串失配时,再撤回若干次匹配,换用长串匹配。
显然,我们至少需要撤回 \(k\) 次短串匹配,然后尝试用长串匹配。
同时,我们声称,我们至多需要撤回 \(k+1\) 次。
-
当撤回 \(k+1\) 次可以匹配,说明 \(c\) 是 \(s_0\) 的真前缀。
-
如果撤回 \(k+2\) 次可以匹配,则撤回 \(k+1\) 次也可以匹配(这是显然的)
那么这就说明 \(s_1+s_0=s_0+s_1\),也就说明 \(c\) 是 \(s_0,s_1\) 的循环节,与 \(s_0,s_1\) 无共同循环节不符。
-
同理撤回更多次的情况也是一样的。
同时,如果撤回 \(k\) 次与撤回 \(k+1\) 次都可以匹配,我们优先撤回 \(k\) 次,因为产生差异只能是撤回用 \(s_1\) 后连用 \(s_1\) 进行匹配,也就是撤回后后面的串被匹配为 \(s_1+s_1\),如果撤回 \(k+1\) 这样做遇上这种情况就被封死了(会连续撤回),而撤回 \(k\) 就不会出现这种情况,并且如果也被封死,撤回 \(k+1\) 次的决策也会寄。
那么我们每次跳跃都是 \(|s_0|\) 的,总复杂度单 log,而长串的匹配由于 \(|s_1|\ge \frac{n}{2}\),因此总匹配次数是 \(O(\frac{m}{n}·n)=O(m)\) 的。