Loading

Lyndon 分解 学习笔记

Lyndon 串

定义

如果一个串的最小后缀就是这个串本身,那这个串是 Lyndon 串。
例:\(abc\)Lyndon 串,而 \(ba\) 不是。

定理

  1. 如果 \(u\)Lyndon 串,\(v\)Lyndon 串,且 \(u < v\),那么 \(uv\) 也是 Lyndon 串。
    证明:如果 \(|u|\) 不是 \(v\) 的前缀,\(uv > v\),得证。
    否则如果 \(uv\) 不是 Lyndon 串,那么 \(v > uv\),那么如果把这两个字符串去掉前 \(|u|\) 个字符,\(v[|u| + 1, |v|] < v\),与 \(v\)Lyndon 串矛盾。
  2. 若字符串 \(A\) 和字符 \(x\) 满足 \(Ax\)Lyndon 串的前缀,且 \(y > x\),那么 \(Ay\)Lyndon 串。
    显然
    证明:如果这个串是 \(A + x + B\)
    \(\forall l \in [2, n], A[l:] + x + B > A + x + B\)\(A[l:] + x \ge A\),因此 \(A[l:] + x > A[l:] + b \ge A\)
    对于 \(l = n + 1\)\(y > x \ge A_1\),成立。

Lyndon 分解

定义一个串的 Lyndon 分解把他划分成一些串 \(A_1 A_2 A_3 ... A_m\),满足:

  1. \(A_1, A_2, ... A_m\) 都是 Lyndon 串。
  2. \(\forall i \in [1, m - 1], A_i \ge A_{i + 1}\)

Lyndon 分解的存在性

可以先把一个串 \(S\) 拆分成 \(|S|\) 个字符。

如果存在一个串 \(A_i < A_{i + 1}\),那么可以把 \(A_i\)\(A_{i + 1}\) 合并起来。这样就得到了一个 Lyndon 分解。

Lyndon 分解的唯一性

如果存在两个不同的 Lyndon 分解:

\(A_1 A_2 A_3 ... A_m\)

\(B_1 B_2 B_3 ... B_m\)

找到第一个不满足 \(A_i \neq B_i\) 的位置 \(i\)。不妨 \(|A_i| > |B_i|\)。如果 \(A_i\)\(B\) 中是 \(B_i B_{i+1} B_{i + 2} ... B_{k}[: l]\)

那么 \(A_i < B_{k} [:l] \le B_{k} \le B_{k - 1} \le ... \le B_{i} < A_i\),矛盾。

Duval 算法

维护三个变量 \(i, j, k\)\([1, i - 1]\) 是确定了的分解(\(s_1 s_2 ... s_d\))。\(s[i, k - 1]\) 是待确定的分解,形式是 \(A^x B\),满足 \(s[i, k - 1] > s_d\) 。让 \(j\) 为和 \(k\) 匹配的指针,为 \(k - |A|\)

接下来分三类讨论:

  1. 如果 \(s_j = s_k\),那么匹配成功,继续匹配
  2. 如果 \(s_j < s_k\),那么 \(s[i, k]\) 是一个 Lyndon 串(Lyndon 串的定理 \(2\)),作为一个新的循环。
  3. 如果 \(s_j > s_k\),那么 \(s[i, k]\) 不是一个 Lyndon 串了,于是可以把这个 \(h\)\(t\) 给确定出来作为 Lyndon 串,然后再进行匹配。

代码

#include<bits/stdc++.h>
#define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++) 
#define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
#define ll long long
#define ull unsigned long long 
#define db double
#define pii pair<int, int>
#define mkp make_pair
using namespace std;
const int N = 5e6 + 7;
int n, ans;
char s[N];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> (s + 1), n = strlen(s + 1);
	int j, k;
	for(int i = 1; i <= n; ) {
		j = i, k = i + 1;
		while(k <= n && s[j] <= s[k]) 
			if(s[j] == s[k]) j ++, k ++;
			else j = i, k ++;
		while(i <= j) i += k - j, ans ^= i - 1;
	}
	cout << ans << endl;
	return 0;
}
posted @ 2021-01-15 20:25  zhoukangyang  阅读(200)  评论(0编辑  收藏  举报