题意简述
请你在一个字符串中找到最大的,使得存在长度为的前缀,后缀和子串,三者没有一点交集,且字符串值相等。算法必须线性(数据水,网上会被卡成n^2的算法也过了)(这是全网为数不多几个严格线性的题解)
思路框架
建一颗树。然后用树上操作解决问题。
具体思路
我们用中的数组建一颗树,从到连一条边。不难发现,就是根节点。
然后,我们先解决问题的几个部分解决。
前缀=后缀
前缀等于后缀并且位置不相等(即:长度不是n)。那满足条件的长度一定在号节点(不含)到根节点(含)的路径上。我们知道,中满足前缀=后缀的长度就是fail[n],fail[fail[n]]…0。也就是从到根的路径了。
前缀=某个子串
然后我们要找到一个子串和它们相等。(的)子串,就是(某个)前缀的(某个)后缀。而(某个)前缀的(某个)前缀还是(的)前缀。
所以,如果的一个以结尾的子串和的某个前缀相等,这个长度一定在到根的路径上。
合并
那么我们发现三者相等。所以,这个要求的长度,即在到根的路径上,也在到根的路径上。那么它就在和的到根的路径上。
稍微一想,长度最长,就是最深。我们要求最深,有这样一个方法:给每个点一个点权,初始为。然后从到的路径上都加上。对于每个,我们询问到根路径上点权的和,就是和的的深度。然后我们只需要维护一个树上前缀和,然后找到前缀和最大的位置即珂。
别忘了三者不能有相交
那咋整嘛。首先,前缀,后缀还有子串不能相交,那么长度就小于等于n的三分之一。由于我们我们在用上面的方法给点权+1的时候,判一下这个点的编号是否小于n的三分之一,如果满足,那才+1。
然后我们找到之后,不断判断的长度是否小于的三分之一,如果小于就跳。当然,我们在找的时候,由于只有一组数据,我们也是一样的找法。用代码写,就是:
1 | while(LCA上点权为0) 跳fail; |
然后就是我们要求的最长长度了。记得要判一下无解。
时间复杂度
我们发现,我们刚刚说到了这样几个操作:
- 求字符串的fail
- 树上一条链加值
- 树上求前缀和
这些都是完成的操作。所以我们的算法是严格的,连都不带。
代码:
1 |
|
代码中的问题答案:不会,因为显然val[0]=1.