最小表示法与Lyndon 分解 学习笔记

最小表示法

最小表示法指一个串所有循环同构串中字典序最小的一个
可以用这样的算法在线性时间内求出最小表示法

首先复制一份在最后
用两个指针一前一后进行扫描,找到第一个不一样的地方,比如 a[i+k]>a[j+k],这时说明 i...k 都不可能成为最小表示法了,那么将 i 移动到 i+k+1 即可

int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i+n]=a[i]=read();
	}
	int i=1,j=2;
	while(i<=n&&j<=n){
		int k=0;
		for(;k<n&&a[i+k]==a[j+k];k++);
		if(k==n)break;
		if(a[i+k]>a[j+k]){
			i=i+k+1;
			if(i==j)i++;
		}
		if(a[i+k]<a[j+k]){
			j=j+k+1;
			if(i==j)j++;
		}
	}
	ans=min(i,j);
	for(int i=ans;i<=ans+n-1;i++){
		printf("%d ",a[i]);
	}
}

Lyndon 分解

首先定义是最小表示法(严格)的字符串为 Lyndon
Lyndon 分解指的是对于一个字符串分解为若干子串,使得每一个子串都是 Lyndon 串,并且字典序不升

可以证明每个串都有唯一的分解方式

  • 引理 1:如果 uv 都是 Lyndon 串并且 u<v,则 uv 也是 Lyndon 串。

  • 引理2:若字符串 v 和字符 c 满足 vc 是某个 Lyndon 串的前缀,则对于字符 d>cvdLyndon 串。

可以画出来手模,这里就不放证明了

  • Duval 算法:

可以结合 这篇博客 里的图理解

首先将串维护成三个部分 s1,s2,s3,第一部分已经分解完成,s2 是一个循环节为 l 的串(末尾为循环节前缀),第三个是待处理部分
维护出三个指针 i,j,k 分别指向目前已经分解完成的部分,上一个循环节这一位置,以及未处理的第一个位置
现在试图把 s3 的第一个字符放入 s2
如果 aj==ak,那么满足循环性质,j 指针后移即可
如果 aj<ak,并不满足性质,那么直接将 s2sk 合并在一起成为一个新的 Lyndon 串,将 j 回归到 s2 开头
如果 aj>ak,意味着整个块满足了性质,那么把每一个循环节拿出来当做分解出来的块,即移动 i 的过程

  • 最小表示

Lyndon 分解可以求出最小表示,可以这样来求:
s 复制一倍在后面,寻找分解中跨过拼接点的一个 Lyndon 串,那么这个串的起点就是最小表示法的起点

int main(){
	scanf("%s",a+1);
	n=strlen(a+1);
	for(int i=1;i<=n;){
		int j=i,k=i+1;
		while(k<=n&&a[k]>=a[j]){
			if(a[k]>a[j])j=i;
			else j++;
			k++;
		}
		while(i<=j){
			ans^=i+k-j-1;
			i+=k-j;
		}
	}
	cout<<ans;
	return 0;
}
posted @   y_cx  阅读(128)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示