一些字符串

P4770 [NOI2018] 你的名字

如果l为1的话很好做(对S和T建立SAM,然后将T在S行匹配,添加一个字符的时候只需要看sam上对应的节点有没有对应的出边:

如果有对应的出边我们直接走上去并且让匹配长度+1,

否则我们不停的跳parent树直到parent树上一个节点有对应的出边为止,此时我们把匹配长度更新为parent树上节点len值+1。

如果跳到了根依然失配那么我们将匹配长度更新为0

这样我们就求出了T的每一个前缀的匹配长度了

那么我们有了这个匹配长度之后有什么用呢?

我们在T的后缀自动姬的每一个节点上维护一个ans值表示这个节点的最长合法长度,

解释一下ans的含义就是这样

我们知道给定一个后缀自动机节点再给定一个字符串长度可以唯一确定一种本质不同子串,那么一个节点的ans值就表示所有长度小于ans并且也被这个节点表示的字符串都是S的子串

我们在S的后缀自动机上跑匹配的同时我们也在T的后缀自动机上跑T这个串,并且每次匹配串的长度减小的时候我们在T的parent树上跳直到这个节点的len值变的合法,然后用当前匹配的长度去更新这个点到根路径上节点的ans值

显然暴力跳链复杂度是假的,但是我们发现当一个点的ans值等于len值的时候这个节点的祖先的ans值也全部等于len值,因此我们暴力跳链如果这个点的ans值等于len我们就停止跳链,这样复杂度就真了

最后的答案就是所有点ans值-父亲的len值之和了,当然由于我们做了个补集转化还要用本质不同的子串数目减去我们求出的数才能输出

但是难点在于一个区间你得建出来区间单独的SAM,会爆炸。

但是发现,我们只是借助S的后缀自动机求出了T的每个前缀的匹配长度

我们在L=1,r=∣S∣所用的算法仅仅对S的后缀自动机执行了这样几个操作

1.检查S的一个节点是否有某一个字符的出边,如果有那就转移上去

2.跳parent树

3.读取一个节点的len值

所以我们只需要实现这些操作即可。

用线段树合并求出来每个结点的endpos,然后我们发现:

当我们判断是否存在一条在(l,r)意义下指向p的转移边的时候,我们只需要查找节点p的righ集合在(1,r)的区间最大值然后和l进行比较如果这个最大值比l小就证明这个转移边在(l,r)作为模板串的时候并不合法,不能进行转移

同样的,当我们读取一个节点的len值的时候我们还是找出(1,r)的区间最大值,在(l,r)作为模板串的意义下,这个节点的len值应该是min(len,l−maxpos(1,r)+1)

至于跳parent树的过程,我们仔细研究一下parent树的定义就会发现其实在整个大的后缀自动机上跳就肥肠符合我们的需要了,所以跳原来后缀自动机上的parent树即可


P2178 [NOI2015] 品酒大会

建出来后缀数组,然后插入维护。

答案求有多少个串的lcp(i,j)==r(其中r为1~n中每一个数)

lcp(i,j)==r 又等价于min(he[i+1],he[i+2],……,he[j])

于是我们把问题重新考虑,就转化成了:求出he数组,然后问有多少个数对,满足i−j的he的最小值恰好等于r

我们把he数组降序排一遍序,然后按照顺序插入

假设我现在的数列长这样(还未被插入的数(即比r小的数)为*):5 4 ∗ 4 5 6 7 ∗ ∗ 4 ∗ 8

然后我现在要在第三个位置插入一个3

不难发现,一共有(2+1)∗(4+1)−1=14种方案(+1是因为3本身也可以算进来, -1是因为[3,3]不能算)

所以这个合并的过程类似于并查集,我们只需要维护一个size就可以求出第一问了


P2463 [SDOI2008] Sandy 的卡片

差分是显然

对最短的串建立SAM

然后考虑匹配,对于每个SAM的节点,对于每个文本串T,都求出来最大匹配长度,每次匹配取min,答案取max


P4555 [国家集训队] 最长双回文串

其实这是Manacher


P4094 [HEOI2016/TJOI2016] 字符串

其实是线段树合并求endpos板子提。

posted @ 2025-07-03 21:41  Dreamers_Seve  阅读(10)  评论(0)    收藏  举报