border 专项训练
A
显然可以分治计算,我们只需要计算跨过中点(含 \(t\) 也就是的匹配次数,就可以递归处理)
那么这就相当于是枚举匹配位置,查前面的后缀和后面的前缀能否匹配,可以暴力地贪心扩展若干轮直到 \(|s_i|\ge 10^6\),这时候直接利用哈希判断即可。
复杂度大概是 \(O(\sum |w|k)\) 的?可能稍卡,但当层数较大时,我们会发现本质不同的 \(t_i\) 只有 \(26\) 个,所以 \(k\) 是可以降下来的。
不过也可以推一推贡献系数来做?
B
设 \(f^k(i)\) 为 \(i\) 跳了 \(k\) 次 \(nxt\) 得到的东西,设 \(S(i)=\lbrace f^j(i)|j\in N,f^j(i)>0\rbrace\),要求即为:
考虑 \(A\) 是一个附加系数,可以拿出来,不妨设 \(g(i)=\sum_{j\in S_i}B_{i-j+1}\)
问题变为求出 \(g(i)\)。
观察下标形式 KMP 算法可以发现,若 \(x\in S(i),x>1\) 则 \(x-1\in S(i-1)\),因为你是一层层叠起来的,第一层是 \([1,nxt_{i-1}+1]=[i-nxt_{i-1},i]\implies [1,nxt_{i-1}]=[i-nxt_{i-1},i-1]\),可以归纳打表证明,这些是不需要改动的,所以我们只需要从 \(g(i)\) 去掉一部分。(其实可以放在 \(nxt\) 树的树链上来看)
而 \(x=1\) 行不行是可以特判的,我们只考虑剩下的部分。
也就是设 \(T(i)=\lbrace x+1|x\in S(i-1),x+1\notin S(i)\rbrace\)
求出 \(T\) 并减去即可,显然总的 \(T\) 的变化量是 \(O(n)\) 的。
怎么打表发现只会删较小部分啊
根据 \(S(i)=S(nxt_i)\cup\lbrace i\rbrace\),考虑 \(T(i),T(nxt_i)\) 的关系。
啊,我们发现 \(T(nxt_i)\subseteq T(i)\)。虽然这近乎显然
- \(nxt_i\in S(i)\implies nxt_i-1\in S(i-1)\)
则有 \(x\in S(nxt_i-1),x+1\notin S(nxt_i)\implies x\in S(i-1),x+1\notin S(i)\)
然后多出来的部分是什么呢?我们就只需要考虑 \(S(i-1)\) 在 \([nxt_i,i-1]\) 这部分的值了。
而且也恰恰是这一部分值,拿出来计算就好了?你找到第一次跳到 \(nxt_i-1\) 的那个次数 \(c\),将 \([0,c-1]\) 的全部加入 \(T\) 即可。
总变化量 \(O(n)\)
C
感觉和 Two Permutations 是一个题啊。
做 \(a_{p_i}=i\),也就相当于是 \(h\) 的那个子串离散化后与 \(a\) 相同
考虑指针扫描,加入一个数字相当于:
- 查找它的排名,将大于它的元素的排名加一
删除一个也基本一样
利用权值线段树维护位置哈希即可。
D
首先可以拿到特殊情况:无周期的串,显然有 0000…01
这种形式,而全周期的串显然有 111111/000000
这种形式
一个有完整周期的串可以由那个周期,也就是 \(AA\dots AB\) 求解 \(AB\) 还原,可以划分子问题。
接着考虑一个有完整周期的串,也就是 \(2(i-nxt_i)\ge i\implies 2nxt_i\le i\),这个串可以表达为一个不完整循环节,也就是 \(ASA\) 的形式,递归求出 \(A\)(用最大的那个 border 进行递归,这样可以保证有更优的策略可以递归到更小的border从而保证最优性),贪心地给 \(S\) 全放 \(0\)。
但是可能会出现问题,也就是形如 000 + 00 + 000
之类的情况,也就是 \(nxt\) 会变小。
不过一个很显然的地方是答案序列第一项肯定是 \(0\),所以我们给 \(S\) 的最后一个位置设置为 \(1\) 即可阻止这类匹配。同时由于 \(S\) 仅有这一个 \(1\),也可以保证不出现新的 border。
E
作为答案的肯定是个 border,而我们所需的是最短的,将其与原串匹配可以覆盖整个原串的border。
考虑 \(dp\),设 \(f_i\) 为 \([1,i]\) 所需最短 \(border\),\(g_i\) 为 \([1,i]\) 可以覆盖到最远位置。
- \(f_1=g_1=1\)
- 若 \(i-nxt_i\le g_{f_{nxt_i}}\),则可以扩展到 \([1,i]\),更新 \(f_i=f_{nxt_i},g_{f_{nxt_i}}=i\)
- 否则只能取自己,\(f_i=g_i=i\)
F
其实就是每个前缀跳 \(nxt_i\) 到底的那个值的和。暴力跳 \(nxt_i\),利用并查集进行路径压缩即可。
G
可以想到一个东西:在字符串 \(S\) 后任意接字符串 \(T\) 可能成为最小后缀的位置至多有 \(O(\log n)\) 个。
有效最小后缀假设存在两个 \([p,n],[q,n]\) 且 \(len(p)\ge 2len(q)\),则显然 \([q,n]\) 是 \([p,n]\) 的 border
那么可以假设 \([p,n]=TTS,[q,n]=TS\),外加一个 \(X\),若 \(TTSX>TSX\implies TSX>SX\) 而这当然是不可能的,因为 \(S\) 优于 \(TS\) 与假设不符
有个想法就是动态 \(i:1\to n\) 维护这些位置集合,每次比较取最优解并删除非法元素
比较这一步如果可以 \(O(1)\) 那也就给出了一个 \(O(n\log n)\) 做法
可以给出Z函数来做,因为有效后缀肯定是互为前缀的 。
对于当前的有效后缀 \([p,i],[q,i]\),假设 \(p<q\),则 \([p,p+i-q]=[q,i]\),后面就转化为了后缀 \([p+i-q+1,i]+[1,i-1]\) 与 \([1,i]\) 的大小关系判断。
利用 Z 函数找到第一个不同位置判断即可。
需要注意的是可能 \(p+i-q+1+z_{p+i-q+1}>i\),这时候相当于跳到了 \(i-(p+i-q+1)+2\) 这个后缀比较了。
判断非法元素是如何做的呢?
很简单啊,所有的串都会新增一个结尾,仅比较这个位置即可,然后还需要用长度来判断一下。