Loading

Lyndon 分解小记

来个简单的。

概念

  • Lyndon 串:一个字符串 \(s\) 被称为 Lyndon 串,当且仅当 \(s\) 整个串是所有后缀中严格最小的

例如 \(\mathtt{ababb},\ \mathtt{abcdefg}\)

  • Lyndon 分解:将字符串 \(s\) 分解为 \(w_1,w_2,...,w_k\),满足 \(w_1 \ge w_2 \ge ... \ge w_k\),并且 \(w_1,w_2,...,w_k\) 都是 Lyndon 串。

例如 \(\mathtt {acaababc}\) 可以分解为 \(\mathtt {ac,aab,abc}\)

性质

对于 Lyndon 串 \(u,v\),若 \(u<v\),那么 \(uv\) 也是 Lyndon 串

证明
  • \(u\) 不是 \(v\) 的前缀,那么一定有 \(uv<v\)

  • \(u\)\(v\) 的前缀,由于 \(v\) 是 Lyndon 串,那么 \(v\) 中以位置 \(|u|+1\) 开头的后缀 \(> v\),进而知 \(uv<v\)

一个字符串 \(s\) 的 Lyndon 分解存在且唯一

存在性证明

\(w_1,w_2,...,w_{|s|}\) 分别为 \(s_1,s_2,...,s_{|s|}\),这些串都是 Lyndon 串。

对于 \(i<|s|\),如果 \(w_i < w_{i+1}\),那么合并 \(w_i,w_{i+1}\)。由性质①可知,合并后的串仍是 Lyndon 串。

一直合并,最后一定有 \(w_1 \ge w_2 \ge ... \ge w_k\)

唯一性证明

使用反证法,若 \(s\) 有两种 Lyndon 分解:

  • \(s = w_1w_2...w_{i-1}w_iw_{i+1}...w_k\)

  • \(s = w_1w_2...w_{i-1}w_i'w_{i+1}...w_j'...w_{k'}\)

钦定 \(|w_i| \ge |w_i'|\),并且 \(w_i = w_i'w_{i+1}'...w_{j-1}'\text{pre}(w_j')\)

那么 \(w_i \ge \text{pre}(w_j')\),设 \(\text{pre}(w_j')\)\(w_i\) 中的开头位置为 \(p\),则 \(w_i \ge「w_i \text{ 中以位置 } p \text{ 开头的后缀}」\)

这与 \(w_i\) 是 Lyndon 串矛盾。

Duval 算法

Duval 算法以 \(O(n) - O(1)\) 的复杂度求出了一个字符串的 Lyndon 分解。

我们维护三个指针 \(i,j,k\),把 \(s\) 分成三段,分别为 \(s_{1...i-1},s_{i...k-1},s_{k...n}\)

同时维护 \(s_{i...k-1}\) 的一个周期 \(p=k-j\),即 \(s_{i...k-1} = s_{i...i+p-1} ^ t + \text{pre} (s_{i...i+p-1})\),并且 \(s_{i...i+p-1}\) 是 Lyndon 串。

使用增量法,目前考虑加入字符 \(s_k\),分三种情况:

  • \(s_j = s_k\)

周期 \(p\) 仍然合法,令 \(j\gets j + 1\)

  • \(s_j < s_k\)

可知 \(s_{i...k}\) 是一个 Lyndon 串,重新设置周期长度,令 \(j\gets i\)

  • \(s_j > s_k\)

本轮分解完毕,得到 Lyndon 串 \(s_{i...i+p-1},s_{i+p...i+2p-1},s_{i+2p,i+3p-1},...\)

每轮开始时,令 \(j\gets i,\ k\gets i + 1\)

模板题

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define fi first
#define se second
#define pir pair <ll, ll>
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn = 5e6 + 10, inf = 1e17;
char s[maxn];
ll n, ans;
int main() {
	scanf("%s", s + 1); n = strlen(s + 1);
	for(ll i = 1; i <= n;) {
		ll j = i, k = i + 1;
		while(k <= n && s[j] <= s[k]) {
			if(s[j] == s[k]) ++j;
			else j = i;
			++k;
		}
		while(i <= j) {
			ans ^= i + k - j - 1;
			i += k - j;
		}
	}
	printf("%lld", ans);
	return 0;
}

这玩意可以直接求解最小表示法,这里懒得写了。

posted @ 2024-05-10 19:03  Lgx_Q  阅读(13)  评论(0编辑  收藏  举报