循环字符串的最小表示法,即:
对于一个字符串S,求S的循环的同构字符串S’中字典序最小的一个。
思路:首先把S复制一份接在它的结尾,得到新串SS。
那么每个循环串可以表示为SS[ i ~ i+n-1 ](记为b[ i ])。
比较 b[ i ] 和 b[ j ] 。如果 i+k位 > j+k位,则 b[ i ] 一定不是最小同构串。
还可以得知:b[ i+1 ] ~ b[ i+k ] 也同样不是最小同构串。
因为对于 1<=p<=k ,存在一个比其更小的 b[ j+p ] 。
所以,b[ i+1 ] ~ b[ i+k ] 可以直接跳过。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//循环同构串的最小表示法(kmp思想)
char s[2000005];
int main(){
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++) s[n+1]=s[i]; //字符串复制一倍
int i=1,j=2,k;
while(i<=n&&j<=n){
for(k=0;k<n&&s[i+k]==s[j+k];k++); //寻找不相等的第一个
if(k==n) break; //s只由一个字符构成
if(s[i+k]>s[j+k]){ //i位置开头已经不可能是最小
i=i+k+1; //跳过i~i+k
if(i==j) i++; //**特判情况**
} else{ //j位置开头已经不可能是最小
j=j+k+1; //跳过j~j+k
if(i==j) j++;
}
}
int ans=min(i,j); //最后一次比较中,答案相对大的那一个被+1
//↑↑↑那么位置较小的就是最小表示的起点位置
printf("%d\n",ans);
return 0;
}
——时间划过风的轨迹,那个少年,还在等你。