最小表示法

最小表示法

定义

argmini=1n(SiSn+S1Si1)。通俗地说,不断将字符串末尾的字符移到开头,得到的 n 个字符串中的字典序最小者即为字符串的 最小表示

暴力求法

在得知 i[1,k) 时使得 SiSn+S1Si1 最小的 i 时,可以直接将其和 SkSn+S1Sk1 比较,得出 i[1,k] 时使得 SiSn+S1Si1 最小的 i。虽然随机数据下表现良好,但是可以构造形如 aaab 的数据将其卡成 O(n2)

后缀数组

求出 S1Sn+S1Sn 的后缀数组,然后 1nrk 最小者即为最小表示。证明显然。

O(n) 做法

原理

f(i)=SiSn+S1Si1,若 f(i)>f(j)f(i)1f(i)k=f(j)1f(j)k(令 g(i,j)=maxk=1n[f(i)1f(i)k=f(j)1f(j)k]k),则 p[1,k],f(i+k)>f(j+k)。也就是 p[i,i+k]p 均不会成为最小表示。

做法

仍然是维护当前的最小表示 i 和对应的集合 [1,j]。比较 f(i)f(j+1),如果 f(i)<f(j+1),则将 j 赋为 j+g(i,j+1)+1,否则将 i 赋为 j+1,将 j 赋为 max(j+1,i+g(i,j+1)+1)

时间复杂度

模板代码

点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=300010;
inline int Md(int x){return x-(x>=n)*n;}
int n,i,j,k,p;
int a[maxn];
int main(){
scanf("%d",&n);
for(i=0;i<n;++i) scanf("%d",a+i);
i=0;
for(j=1;j<n;++j){
k=0;
while(k<=n&&a[Md(i+k)]==a[Md(j+k)]) ++k;
if(a[Md(i+k)]>a[Md(j+k)]){
p=j;
j=max(j,i+k);
i=p;
}
else j=j+k;
}
for(j=0;j<n;++j) printf("%d ",a[Md(i+j)]);
return 0;
}
posted @   Fran-Cen  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示