Loading

Lyndon 分解

参考 https://www.luogu.com.cn/article/lt2rnl6d

Lyndon 串:字符串 \(S\) 是 Lyndon 串当且仅当 \(S\)\(S\) 所有后缀中最小的。

Lyndon 分解:将 \(S\) 分解成 \(s_1s_2\ldots s_k\),满足 \(s_i\) 是 Lyndon 串,并且 \(s_i\ge s_{i+1}\)

引理 \(1\):如果字符串 \(u,v\) 都是 Lyndon 串,并且 \(u<v\),那么 \(uv\) 也是 Lyndon 串。

\(uv < v\),那么 \(uv\) 就是 Lyndon 串,对于 \(v\) 所有后缀 \(v^\prime\ge v>uv\),即正确。分类讨论:

  • \(|u|\ge|v|\),那么在第 \(|v|\) 个字符之前 \(u\)\(v\) 就已经出现了不同字符,\(uv < v\)
  • \(|u|<|v|\),若 \(u\) 不是 \(v\) 的前缀,那么和上面一样,\(uv<v\)。否则,因为 \(v\) 是 Lyndon 串,\(v<v[|u|+1:]\),那么 \(uv<v\)

引理 \(2\):Lyndon 分解有且仅有一个。

存在性:考虑构造方案,先字符串分解成单个字符,若 \(s_i < s_{i+1}\),则合并 \(s_i\)\(s_{i+1}\),所有必定有解。

唯一性:假设有两个不同的分解方案,如下

\[S=s_1s_2\ldots s_is_{i+1}s_{i+2}\ldots \]

\[S=s_1s_2\ldots s_is^\prime_{i+1}s^\prime_{i+2}\ldots \]

不妨设 \(|s_{i+1}|>|s^\prime_{i+1}|\)。可知 \(s_{i+1}=s_{i+1}^\prime\ldots s_{i+k}^\prime[:l]\),那么可以得到以下:

\[s_{i+1} < s_{i+k}^\prime[:l] \le s_{i+k} \le s_{i+1}^\prime <s_{i+1} \]

明显矛盾,故 Lyndon 分解只存在一个。

引理 \(3\)\(c\) 是一个字符,\(vc\) 是一个 Lyndon 串的前缀,若字符 \(d\) 满足 \(d>c\),那么 \(vd\) 是一个 Lyndon 串

因为 \(v[i:]+d>v[i:]+c>vd\),故成立。

Duval 算法

考虑已经确定了前缀 \(i-1\) 的 Lyndon 分解,目前考虑 \(S[i,k]\)\(S[i,k-1]\) 可以分解成 \(t^hv\)\(t\) 是 Lyndon 串,并且 \(v\)\(t\) 的前缀或空串,并且 \(s_g>t\)。设 \(j=k-|t|\)

考虑加入字符 \(s_k\)

  • \(s_j=s_k\)\(t\) 保持不变,\(|t^hv|\) 增大 \(1\)
  • \(s_j<s_k\),让 \(t^hv\) 成为新的 \(t\)
  • \(s_j>s_k\),让 \(h\)\(t\) 成为新的分解串,让 \(v\) 变成新的 \(t\)
#include<cstdio>
#include<cstring>
#define N 5000010
using namespace std;
int n;
char s[N];
int main()
{
	scanf("%s", s + 1);
	n = strlen(s + 1);
	int ans = 0;
	for(int i = 1; i <= n;)
	{
		int j = i, k = i + 1;
		while(k <= n && s[j] <= s[k])
		{
			if(s[j] < s[k])j = i;
			else j++;
			k++;
		}
		while(i <= j)
		{
			ans ^= i + k - j - 1;
			i += k - j;
		}
	}
	printf("%d\n", ans);
	return 0;
}

posted @ 2025-03-19 15:55  larsr  阅读(42)  评论(0)    收藏  举报