P1368 【模板】最小表示法 题解

那就是最小表示法

最小表示法就是给你的一个可以循环的字符串,找到以某一个字符为开头时字符串的字典序最小。

例如cbad的最小表示法即为adcb

一、暴力

很简单的思想,以每一个字符为开头进行扫描,得到最小表示

具体地,我们可以设置两个指针$i,j$,用$i$表示当前最小表示的开头,$j$作为循环指针

按位进行比对时,可以设置第三个指针$k$,那么每一次比对的字符就是$i+k$和$j+k$

接下来就不用多说了吧

二、优化

上述算法最慢的地方在于比对完之后k又要重置,并不能利用已经算出的大小关系,我们对下面这个字符串比对过程进行分析:

AAAAAABCD

i=1,j=2,k=5时,比对到s[6](A)<s[7](B),说明1为开头字典序更小

暴力的做法此时j应该变为3,但是我们可以注意到因为上一次已经比对到了k=5,可以说明:

s[1]=s[2] s[2]=s[3] s[3]=s[4] s[4]=s[5] s[5]=s[6] s[6]<s[7]

当$j\in[2,6]$时,必然也比以i为开头小了

所以我们可以直接让j=k+1

(妙不可言)

三、代码

#include<iostream>
using namespace std;
int n,a[1000001];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i];
    int i=1,j=2,k=0;
    while(i<=n&&j<=n&&k<=n)
    {
        if(i==j)
        {
            j++;
            continue;
        }
        int x=i+k,y=j+k;
        if(x>n)x-=n;
        if(y>n)y-=n;
        int t=a[x]-a[y];
        if(t==0)k++;
        else
        {
            if(t>0)i=i+k+1;
            else j=j+k+1;
            k=0;
        }
    }
    for(int l=i;l<=n;l++)
    cout<<a[l]<<' ';
    for(int l=1;l<i;l++)
    cout<<a[l]<<' ';
}

 

posted @ 2021-07-12 09:18  lei_yu  阅读(51)  评论(0编辑  收藏  举报