【luogu P6114】【模板】Lyndon 分解(Duval算法)

【模板】Lyndon 分解

题目链接:luogu P6114

题目大意

求 Lyndon 分解得到的每个区间的右端点的异或和。

思路

有关证明可以查阅 command_block 大神的博客,从那里学的/fad。

才发现,后缀自动机只是字符串难的开始。。。
(好吧其实 Border 已经不会了)

定义

Lyndon Word

如果一个字符串 s s s 满足它的所有后缀 t t t(不包括 s s s)都有 s < t s<t s<t(字典序),那 s s s 就是关于 < < < 的一个 Lyndon Word。

你也可以在字符集 ∑ \sum 上定义相反的全序关系 < 0 , < 1 <_0,<_1 <0,<1(比如正反字典序),这样就是关于 < 0 / 1 <_{0/1} <0/1 的了。

Lyndon 分解

把字符串 s s s 分解成 s 1 , s 2 , . . . , s m s_1,s_2,...,s_m s1,s2,...,sm 都是 Lyndon Word,而且 s 1 ⩾ s 2 ⩾ . . . ⩾ s m s_1\geqslant s_2\geqslant ...\geqslant s_m s1s2...sm

性质

(很多都不会证或者懒得证,感性理解)

  1. Lyndon Word 不会有 Border

显然,因为这样就是后缀,而且又是前缀,那字典序肯定是小于整个串的。

  1. 如果一个 Lyndon Word s = a b s=ab s=ab a < b a<b a<b

不然的话就不满足定义了。( a b < b , a < a b ab<b,a<ab ab<b,a<ab
然后因此也有 a a a 不是 b b b 的前缀的话 a c < b d ac<bd ac<bd a < b a<b a<b 是充要条件。

  1. 对于 Lyndon Word a a a 如果有 b < a b<a b<a,有 b a < a ba<a ba<a

挺显然的,就分 b b b 是否是 a a a 的子串来讨论即可。

  1. 如果有一个字符串 s s s 和一个字符 x x x 使得 s x sx sx 是某个 Lyndon Word 的前缀,那对于字典序比 x x x 小的字符 y y y(即 y < x y<x y<x), s y sy sy 也一定是 Lyndon Word。

设那个 Lyndon Word 是 s x t sxt sxt,然后你对于 s s s 的每个位置考虑加上 x x x 是不是 s s s 前缀。
如果是,那改成 y y y 更小(而且互不为前缀),那我们可以用 2 2 2 里面的因此的结论得到 s y < sy< sy< 改成 y y y 得到的串。
如果不是,考虑直接在这个部分接上 t t t,也可以通过那个东西得到接上 y y y > s y >sy >sy 的。
然后两个结合起来就得证了。
(不难看出我其实看不懂这个部分,硬胡了属于是)

  1. s s s 是 Lyndon Word 当且仅当它可以被表示成 a b ab ab,而且 a , b a,b a,b 都是 Lyndon Word,且 a < b a<b a<b

首先 b b b 那边肯定可以,然后看 a a a 那边的。(肯定可以是你传递一下 a < b , b < a<b,b< a<b,b< 你那个串,就可以了)
发现因为没有 Border,所以一定会在 b b b 前面比较完,所以也可以。
这是一个十分重要的信息。

  1. Lyndon 拆分唯一且存在

首先是唯一,这个其实挺显然的,你考虑不同的位置,然后看大的那个会包含另一种若干个全部和一个一部分。
那这个这个一部分在大的那个里面是后缀,就 > > > 了,然后你可以推回去 > > > 大的那些,就矛盾了。
注意到一定会有后面的那个部分,不然要么就一样要么就应该是另一个串的大了。

存在这个就要利用我们的 5 5 5 性质了。
不难看出一个使用于任何一个串构造的方法:
一开始都是一个字符(肯定可以),然后你每次找相邻的前面的比后面的字典序小的合并起来,就可以了。

Duval算法

为什么要用这个呢?
因为你会发现我们上面构造的方法很麻烦。
然后如果你每次选一个最小的后缀的话就要后缀数组排序,不仅是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 也很麻烦。
于是就有了 O ( n ) O(n) O(n) 的简介的 Duval 算法!

你会发现刚刚我们想了后缀,那我们能不能找前缀呢!
其实是可以的,就是 Duval 算法。

然后又一个定理:
s = u k u ′ x s=u^ku'x s=ukux(其中 u ′ u' u u u u 的前缀,可以是空的, x x x 是一个字符,而且不能接在 u ′ u' u,也就是不是这个循环的)
如果 x > x> x> u ′ u' u 下一个应该要是的字符,那根据上面的性质 5 5 5 我们可以知道 s s s 就是一个 Lyndon Word。
如果 x > u ′ x>u' x>u 下一个应该要是的字符,那最长前缀 Lyndon Word 是 u u u
如果选 u k u ′ u^ku' uku 的其中一个前缀(大于 u u u 的),那会有 border 不满足条件。
那唯一一个可能既是 u k u ′ x u^ku'x ukux 了。那我们会发现 u ′ x < u < u k u ′ x u'x<u<u^ku'x ux<u<ukux 所以不行。

那 Duval 就是维护这个 s s s,假设当前已经划分好 1 ∼ p − 1 1\sim p-1 1p1 的,那就是要解决 p ∼ n p\sim n pn,然后当前在 i i i,表示乘了 u c u ′ u^cu' ucu 的形式,我们需要记录的就是 ∣ u ∣ , c , ∣ u ′ ∣ |u|,c,|u'| u,c,u
加入一个 s i + 1 s_{i+1} si+1
如果是要求的字符, u ′ u' u 扩展,并查看是否会贡献给 c c c
如果大于,我们直接把 u = i − p + 1 u=i-p+1 u=ip+1(不要直接统计,因为可能会这个循环)。
如果小于,我们就把 c c c u u u 贡献上,然后你考虑 u ′ u' u 怎么处理,会发现我们不能确定里面会不会循环,所以我们要把 i i i 退回到 u ′ u' u 前的位置(也就是 p p p,因为你贡献 u u u 的时候就加了 p p p

然后考虑分析一下复杂度,每个 u ′ u' u 的长度不会超过 u u u,而且 u u u 每个点只会进入一次,所以最多退 O ( n ) O(n) O(n) 次,总复杂度是 O ( n ) O(n) O(n) 的。

然后要注意的是做完之后我们要处理掉剩下的 u , c , u ′ u,c,u' u,c,u,有一个很好的方法就是 i i i 1 1 1 做到 n n n,然后我们让 s n + 1 = − ∞ s_{n+1}=-\infty sn+1=,那每次必然会小于,然后就可以不断的处理 u ′ u' u 消完它了。

EX

好像说可以用 Duval 算法求字符串每个前缀的最小 / 最大后缀。
还可以求 s s s 的最小表示,就是复制一遍弄个 Lyndon 分解,然后找起始位置在第一个串的最小 Lyndon Word 即可。这个用定义就可以证明。

代码

#include<cstdio>
#include<cstring>

using namespace std;

const int N = 5e6 + 100;
int n, ans, p, u, c, u_;
char s[N];

int main() {
	scanf("%s", s + 1); n = strlen(s + 1);//s[n+1]=-inf 
	
	u = 1; c = 1;
	for (int i = 1; i <= n; i++) {
		if (s[i + 1] == s[i - u + 1]) {
			u_++; if (u_ == u) {c++; u_ = 0;}
		}
		else if (s[i + 1] > s[i - u + 1]) {
			u = i - p + 1; c = 1; u_ = 0;
		}
		else {
			while (c--) {p += u; ans ^= p;}
			u = 1; c = 1; u_ = 0;
			i = p;//u'的部分重新看过 
		}
	}
	printf("%d", ans); 
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值