我们通过跳 fail 指针找到 A 所对应的节点,然后两边添加 X 就到了现在的回文串了(即 XAX),很显然,这个节点就是以 p 结尾的最长回文子串对应的树上节点。(同时,这个时候长度 −1 节点优势出来了,如果没有 X 能匹配条件就是同一个位置的 sp=sp,就自然得到了代表字符 X 的节点。)此时要判断一下:没有这个节点,就需要新建。
当 ∣s∣>1 时,设 t=sc,其中 t 表示 s 最后增加一个字符 c 后形成的字符串,假设结论对 s 串成立。考虑以最后一个字符 c 结尾的回文子串,假设它们的左端点由小到大排序为 l1,l2,…,lk。由于 t[l1..∣t∣] 是回文串,因此对于所有位置 l1≤p≤∣t∣,有 t[p..∣t∣]=t[l1..l1+∣t∣−p]。所以,对于 1<i≤k,t[li..∣t∣] 已经在 t[1..∣t∣−1] 中出现过。因此,每次增加一个字符,本质不同的回文子串个数最多增加 1 个。
border:若 0≤r<∣s∣,pre(s,r)=suf(s,r),就称 pre(s,r) 是 s 的 border。
周期和 border 的关系:t 是 s 的 border,当且仅当 ∣s∣−∣t∣ 是 s 的周期。
证明
若 t 是 s 的 border,那么 pre(s,∣t∣)=suf(s,∣t∣),因此 ∀1≤i≤∣t∣,s[i]=s[∣s∣−∣t∣+i],所以 ∣s∣−∣t∣ 就是 s 的周期。
若 ∣s∣−∣t∣ 为 s 周期,则 ∀1≤i≤∣s∣−(∣s∣−∣t∣)=∣t∣,s[i]=s[∣s∣−∣t∣+i],因此 pre(s,∣t∣)=suf(s,∣t∣),所以 t 是 s 的 border。
引理一
t 是回文串 s 的后缀,t 是 s 的 border 当且仅当 t 是回文串。
证明
对于 1≤i≤∣t∣,由 s 和 t 为回文串,因此有 s[i]=s[∣s∣−i+1]=s[∣s∣−∣t∣+i],所以 t 是 s 的 border。
对于 1≤i≤∣t∣,由 t 是 s 的 border,有 s[i]=s[∣s∣−∣t∣+i],由 s 是回文串,有 s[i]=s[∣s∣−i+1],因此 s[∣s∣−i+1]=s[∣s∣−∣t∣+i],所以 t 是回文串。
下图中,相同颜色的位置表示字符对应相同。
引理二
t 是串 s 的 border (∣s∣≤2∣t∣),s 是回文串当且仅当 t 是回文串。
证明
若 s 是回文串,由引理 1,t 也是回文串。
若 t 是回文串,由 t 是 s 的 border,因此 ∀1≤i≤∣t∣,s[i]=s[∣s∣−∣t∣+i]=s[∣s∣−i+1],因为 ∣s∣≤2∣t∣,所以 s 也是回文串。
引理三
t 是回文串 s 的 border,则 ∣s∣−∣t∣ 是 s 的周期,∣s∣−∣t∣ 为 s 的最小周期,当且仅当 t 是 s 的最长回文真后缀。
引理四
x 是一个回文串,y 是 x 的最长回文真后缀,z 是 y 的最长回文真后缀。令 u,v 分别为满足 x=uy,y=vz 的字符串,则有下面三条性质
∣u∣≥∣v∣;
如果 ∣u∣>∣v∣,那么 ∣u∣>∣z∣;
如果 ∣u∣=∣v∣,那么 u=v。
证明
由引理 3 的推论,∣u∣=∣x∣−∣y∣ 是 x 的最小周期,∣v∣=∣y∣−∣z∣ 是 y 的最小周期。考虑反证法,假设 ∣u∣<∣v∣,因为 y 是 x 的后缀,所以 u 既是 x 的周期,也是 y 的周期,而 ∣v∣ 是 y 的最小周期,矛盾。所以 ∣u∣≥∣v∣。
因为 y 是 x 的 border,所以 v 是 x 的前缀,设字符串 w,满足 x=vw(如下图所示),其中 z 是 w 的 border。考虑反证法,假设 ∣u∣≤∣z∣,那么 ∣zu∣≤2∣z∣,所以由引理 2,w 是回文串,由引理 1,w 是 x 的 border,又因为 ∣u∣>∣v∣,所以 ∣w∣>∣y∣,矛盾。所以 ∣u∣>∣z∣。
u,v 都是 x 的前缀,∣u∣=∣v∣,所以 u=v。
推论
s 的所有回文后缀按照长度排序后,可以划分成 log∣s∣ 段等差数列。
证明
设 s 的所有回文后缀长度从小到大排序为 l1,l2,…,lk。对于任意 2≤i≤k−1,若 li−li−1=li+1−li,则 li−1,li,li+1 构成一个等差数列。否则 li−li−1=li+1−li,由引理 4,有 li+1−li>li−li−1,且 li+1−li>li−1,li+1>2li−1。因此,若相邻两对回文后缀的长度之差发生变化,那么这个最大长度一定会相对于最小长度翻一倍。显然,长度翻倍最多只会发生 O(log∣s∣) 次,也就是 s 的回文后缀长度可以划分成 log∣s∣ 段等差数列。
该推论也可以通过使用弱周期引理,对 s 的最长回文后缀的所有 border 按照长度 x 分类,x∈[20,21),[21,22),…,[2k,n),考虑这 log∣s∣ 组内每组的最长 border 进行证明。详细证明可以参考金策的《字符串算法选讲》和陈孙立的 2019 年 IOI 国家候选队论文《子串周期查询问题的相关算法及其应用》。
有了这个结论后,我们现在可以考虑如何优化 dp 的转移。
优化
回文树上的每个节点 u 需要多维护两个信息,diff[u] 和 slink[u]。diff[u] 表示节点 u 和 fail[u] 所代表的回文串的长度差,即 len[u]−len[fail[u]]。slink[u] 表示 u 一直沿着 fail 向上跳到第一个节点 v,使得 diff[v]=diff[u],也就是 u 所在等差数列中长度最小的那个节点。