最小表示法

最小表示法

定义

\(\text{argmin}_{i=1}^n (S_i\sim S_n+S_1\sim S_{i-1})\)。通俗地说,不断将字符串末尾的字符移到开头,得到的 \(n\) 个字符串中的字典序最小者即为字符串的 最小表示

暴力求法

在得知 \(i\in[1,k)\) 时使得 \(S_i\sim S_n+S_1\sim S_{i-1}\) 最小的 \(i\) 时,可以直接将其和 \(S_k\sim S_n+S_1\sim S_{k-1}\) 比较,得出 \(i\in[1,k]\) 时使得 \(S_i\sim S_n+S_1\sim S_{i-1}\) 最小的 \(i\)。虽然随机数据下表现良好,但是可以构造形如 \(aa\cdots ab\) 的数据将其卡成 \(O(n^2)\)

后缀数组

求出 \(S_1\sim S_n+S_1\sim S_n\) 的后缀数组,然后 \(1\sim n\)\(rk\) 最小者即为最小表示。证明显然。

\(O(n)\) 做法

原理

\(f(i)=S_i\sim S_n+S_1\sim S_{i-1}\),若 \(f(i)>f(j)\)\(f(i)_1\sim f(i)_k=f(j)_1\sim f(j)_k\)(令 \(g(i,j)=\max_{k=1}^n[f(i)_1\sim f(i)_k=f(j)_1\sim f(j)_k]\cdot k\)),则 \(\forall p\in[1,k],f(i+k)>f(j+k)\)。也就是 \(\forall p\in[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 @ 2022-10-10 14:39  Fran-Cen  阅读(67)  评论(0编辑  收藏  举报