最小表示法

简介

请求解:给出一个字符串,求与它循环同构的串中字典序最小的串。
考虑暴力,可能导致(O(n^2))(比如aaa...aaab)。
最小表示法可以(O(n))解决:枚举两个起点,如果这两个起点i,j开始的串在匹配到第k个时不同(假设s[i + k - 1] < s[j + k - 1]),由于s[i + k - 2]对应s[j + k - 2]相等,那么对于任意串Sj+k,(k在[0,k - 1]内)总有Si+k更优。所以可以直接跳过。

#include<iostream>
#include<cstdio>
using namespace std;
inline void read(int &x)
{
    x=0;int f=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x*=f;
}
inline void print(int x)
{
    if(x<0){x=-x;putchar('-');};
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
inline int cread(char a[])
{
    int tot=0;
    char c=getchar();
    for(;c>='a'&&c<='z';c=getchar()) 
    {
        a[tot]=c;tot++;
    }
    return tot;
}//经多次试验发现此输入常出现玄学bug,请无视此函数
char s[10010];
int len=0;
int zxbsf()
{
    int i=0,j=1,k=0,t;//取s开头两位置i,j;k为能重合的最大长度; 
    while(i<len&&j<len&&k<len)
    {
        t=s[(i+k)%len]-s[(j+k)%len];
        if(!t) k++;//i,j 上字符相同 
        else
        {
            if(t>0) i=i+k+1; 
            else j=j+k+1;//哪处字典序大哪处就跳 
            if(i==j) j++;//重合要错位处理 
            k=0;//长度置零 
        }
    }
    if(i>j) return j;
    else return i;
}
int main()
{
    int gg;
    read(gg);
    while(gg--)
    {
        len=cread(s);
        print(zxbsf()+1);
        putchar('
');
    }

} 

推荐oi wiki-最小表示法

原文地址:https://www.cnblogs.com/Thomastine/p/11861732.html