最小表示法与Lyndon 分解 学习笔记
最小表示法
最小表示法指一个串所有循环同构串中字典序最小的一个
可以用这样的算法在线性时间内求出最小表示法
首先复制一份在最后
用两个指针一前一后进行扫描,找到第一个不一样的地方,比如 ,这时说明 都不可能成为最小表示法了,那么将 移动到 即可
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 分解指的是对于一个字符串分解为若干子串,使得每一个子串都是 串,并且字典序不升
可以证明每个串都有唯一的分解方式
-
引理 1:如果 和 都是 串并且 ,则 也是 串。
-
引理2:若字符串 和字符 满足 是某个 串的前缀,则对于字符 有 是 串。
可以画出来手模,这里就不放证明了
- 算法:
可以结合 这篇博客 里的图理解
首先将串维护成三个部分 ,第一部分已经分解完成, 是一个循环节为 的串(末尾为循环节前缀),第三个是待处理部分
维护出三个指针 分别指向目前已经分解完成的部分,上一个循环节这一位置,以及未处理的第一个位置
现在试图把 的第一个字符放入
如果 ,那么满足循环性质, 指针后移即可
如果 ,并不满足性质,那么直接将 与 合并在一起成为一个新的 串,将 回归到 开头
如果 ,意味着整个块满足了性质,那么把每一个循环节拿出来当做分解出来的块,即移动 的过程
- 最小表示
用 分解可以求出最小表示,可以这样来求:
将 复制一倍在后面,寻找分解中跨过拼接点的一个 串,那么这个串的起点就是最小表示法的起点
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效