Lyndon 分解

现在只会 lyndon 分解怎么写。所以先放在这里占坑。以后补 Runs 和 Lyndon Tree 相关知识。

大量抄 pdf 和 cmd 博客。

Lyndon Word 及其相关性质

定义:若字符串 \(s\) 的最小后缀是它本身,则它是一个 Lyndon Word。

  1. Lyndon Word 没有 Border。

证明:如果存在,则 border 作为后缀比它小。
2. 若两个字符串 \(u,v\) 为 Lyndon Word,且 \(u<v\),则 \(uv\) 也是 Lyndon Word。

证明:首先显然开头在 \(|u|\) 前的后缀都比 \(uv\) 大。若 \(|u|\ge|v|\),则在 \(|v|\)\(uv\)\(v\) 就出现了不同,则 \(v>uv\),同时 \(v\) 是 Lyndon Word,则 \(uv\) 的所有后缀都大于它。若 \(|u|<|v|\),则若 \(u\) 不是 \(v\) 前缀,则显然 \(v>uv\)。反之,将 \(v\) 前面 \(|u|\) 个字符去掉大于 \(v\) ,则 \(v>uv\)

  1. 若字符串 \(s\) 和字符 \(x\) 满足 \(sx\) 为某个 Lyndon Word 的前缀,则对 字符 \(y>x\)\(sy\) 是 Lyndon Word。

证明:设 \(sxt\) 是 Lyndon Word,则对于 \(i\ge 2\)\(s[i\rangle xt>sxt\),即 \(s[i\rangle x>s\),因此 \(s[i\rangle y>s\)。而 \(y>x>s_1\),因此 \(sy\) 是 Lyndon Word。

Lyndon 分解

定义:一个串 \(s\) 的 Lyndon 分解为一个字符串序列 \(t_1,t_2,\cdots,t_m\),满足:

  1. \(t_i\) 为 Lyndon Word。
  2. \(t_i\ge t_{i+1}\)

定理:Lyndon 分解存在且唯一。

存在性:构造即证明。初始令 \(m=|s|\),每一个 \(t_i\) 都是单字符。每次找到 \(a_i<a_{i+1}\) 并合并为一个串,最后停止的时候即是合法的 Lyndon 分解。

唯一性:假设存在两个 Lyndon 分解 \(A,A'\),设 \(|A_i|>|A'_i|\),此时 \(A_i=A'_iA'_{i+1}\cdots A'_k\langle l]\)。那么显然有 \(A_i<A'_k\langle l]\le A'_k\le\cdots\le A'_i<A_i\) 矛盾。

Duval 算法

Duval 算法可以在 \(O(n)\) 时间复杂度和 \(O(1)\) 的额外空间内求得一个串的 Lyndon 分解。它维护三个变量 \(i,j,k\),满足任意时刻:

  • \(s\langle i]\) 的 Lyndon 分解已经固定。
  • \(s[i,k-1]=t^x+v(x>1)\) 的分解没有固定,\(t\) 是 Lyndon Word,且 Lyndon 分解确定的上一个串 \(t_g>s[i,k-1]\)\(j=k-|t|\)

当前处理到 \(k\),分三种情况讨论:

  1. \(s_j=s_k\) 时,\(j,k\)\(+1\),仍然不变。
  2. \(s_j<s_k\) 时,\(v+s_k\) 是 Lyndon Word,则不断往前合并,整个 \(s[i,k]\) 固定为 Lyndon Word。
  3. \(s_j>s_k\) 时,\(t^x\) 固定为 Lyndon Word,\(i\) 调整至 \(v\) 的开头。

显然三个指针都是单调的,因此复杂度 \(O(n)\)

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char s[5000010];
int n,ans;
int main(){
	scanf("%s",s+1);n=strlen(s+1);
	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 @ 2023-04-30 15:53  gtm1514  阅读(32)  评论(0编辑  收藏  举报