【暖*墟】 #字符串# 循环同构串的最小表示法
循环字符串的最小表示法,即:
对于一个字符串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 <cmath> #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <algorithm> #include <stack> #include <queue> 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; }