【暖*墟】 #字符串# 循环同构串的最小表示法

循环字符串的最小表示法,即:

对于一个字符串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
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/9337010.html