洛谷P1368 【模板】最小表示法
循环同构:当字符串 \(S\) 中可以选定一个位置 \(i\) 满足
\[S[i···n] + S[1···i-1] = T \]则称 \(S\) 与 \(T\) 循环同构。
最小表示法:字符串 \(S\) 的最小表示为与 \(S\) 循环同构的所有字符串中字典序最小的字符串
原始暴力,\(i\),\(j\) 为两个指针,分别代表当前起始。
这两个表示法互相比较,发现更大的表示法就替换,因此最小的表示法得以保留。
但是显然暴力,可以卡成 \(O(n^2)\)。
考虑一个优化,在匹配至 \(S[i+k]>S[j+k]\) 时,发现 \(S[i] \to S[i+k]\) 均不能成为起始,因为总有 \(S[j] \to S[j+k]\) 的起始可以小于它。
于是我们可以直接 \(i \to i+k+1\) ,这样避免重复计算。
因为最后留下来的肯定是跳的慢的(跳得快的跳出去了算法结束了),所以取一个 \(min\) 。
\(\huge \mathscr{Code}\)
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5+100;
int n,m,num[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>num[i];
num[i+n] = num[i];
}
int i = 1,j = 2,k = 0;
while(i<=n && j<=n && k<=n){
if(num[i+k]==num[j+k]) k++;
else{
if(num[i+k]>num[j+k]) i += k + 1;
else j += k + 1;
if(i==j) j++; // 防止出现一样的表示法
k = 0;
}
}
int ans = min(i,j);
for(int i=ans;i<=ans+n-1;i++) cout<<num[i]<<' ';
return 0;
}

浙公网安备 33010602011771号