Lyndon 分解&最小表示法
Lyndon 分解
P6114 【模板】Lyndon 分解
两个开头:
到边界后可以往后延伸,注意取余判断。如果字符串的开头要取余跑回前面继续找,那么结束比对,直接使用当前最优开头即可。
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<random>
#include<ctime>
using namespace std;
long long r_r(){//快读
long long k=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c)){
k=(k<<1)+(k<<3)+(c^48);
c=getchar();
}
return k*f;
}
const int o_o=3e5+10;
const int m_a=1e9;
int n,a_s,a_a[o_o];
int f_n(){
int i=0,j=1;//两个串开头初始化
int k=0;//辅助比较变量
while(i<n&&j<n&&k<n){
if(a_a[(i+k)%n]==a_a[(j+k)%n])k++;//两个串相同的地方跳过
else{
//其中一个不优,换新串
if(a_a[(i+k)%n]>a_a[(j+k)%n])i+=k+1;
else j+=k+1;
if(i==j)i++;//保证不相等
k=0;//重心从第一位开始比较
}
}
return min(i,j);//返回更小的值
}
int main(){
n=r_r();
for(int i=0;i<n;i++)a_a[i]=r_r();//读入序列
a_s=f_n();//找到开头
for(int i=0;i<n;i++)//输出序列
printf("%d ",a_a[(i+a_s)%n]);
return 0;
}
Lyndon 分解做法
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<random>
#include<ctime>
using namespace std;
long long r_r(){//快读
long long k=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c)){
k=(k<<1)+(k<<3)+(c^48);
c=getchar();
}
return k*f;
}
const int o_o=5e6+10;
int s[o_o];
int n,a_s;
int main(){
n=r_r();
for(int i=1;i<=n;i++){
s[i]=r_r();
s[i+n]=s[i];//复制一份
}
int i=1;
while(i<=n){
int j=i,k=i+1;
while(k<=n*2&&s[j]<=s[k]){
if(s[j]<s[k])j=i;
else j++;
k++;
}
while(i<=j){
i+=k-j;
if(i<=n)a_s=i;//更新开头(越靠后的字典序越小)
}
}
for(int i=1;i<=n;i++)printf("%d ",s[a_s-1+i]);
return 0;
}