[总结] 字符串的最小表示
[总结] 字符串的最小表示
给定一个字符串 \(S\),如果不断把它的最后一个字符放到开头,会形成 \(n\) 个字符串,它们循环同构。
这些字符串中字典序最小的,叫做字符串 \(S\) 的最小表示。
算法分析
板子题
对于如何构造,首先要先把字符串复制一倍,设 \(B[i]\) 表示 \(S[i-i+n-1]\),则每一个 \(B[i]\) 都是一个字符串的循环同构串的表示。
算法流程基于 双指针 思想:
- 维护两个指针 \(i\) 和 \(j\),初始时令 \(i=1,j=2\) ,分别表示维护的两个循环同构串。
- 维护一个值 \(k\),表示在 \(k\) 这个距离 \(S[i+k]\not =s[j+k]\),分两种情况考虑:
- \(S[i+k]<s[j+k]\),说明 \(i\) 更优,并且 \(j-j+k\) 都不可能成为最小表示的开头了,那么令 \(j=j+k+1\) 。
- 否则令 \(i=i+k+1\) 。
注意:如果跳转过程中出现了 \(i=j\) 的情况,那么令跳跃的那一方再进 \(1\) 。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
}
return fl?-x:x;
}
const int maxn = 1e4 + 10;
char s[maxn<<1];
int T,ans;
int main(){
T=read<int>();
while(T--){
cin>>s+1;
int n=strlen(s+1);
for(int i=1;i<=n;i++)s[n+i]=s[i];
int i=1,j=2,k=0;
while(i<=n && j<=n){
for(k=0;k<n && s[i+k]==s[j+k];k++);
if(k==n)break;
if(s[i+k]>s[j+k]){
i=i+k+1;if(i==j)i++;
}
else{
j=j+k+1;if(i==j)j++;
}
}
ans=min(i,j);
printf("%d\n",ans);
}
return 0;
}