HDU3374

题目链接:HDU3374

题意:给出一个字符串,依次左移一个单位形成一堆字符串,求其字典序最小和最大的字符串需要左移多少位,以及一共有几个这样的字符串。

题目分析:首先可以确定两个字符串出现的次数应该相同。由于假设最小的左移m位得到最大的话,那么所有相同的最小字符串左移m位都会得到最大串。对于求解最小最大串的位置可以用最小最大表示法。

最大最小表示法:总的来说就是这道题的模板,求一个循环串字典序的最小和最大的位置。算法整体思想很简单,以最小为例如题,指针i,j分别代表比较的两个串的串首位置,这两个位置是互相更新的。当s[i+k]==s[j+k]时,k++,当s[i+k]<s[j+k]时,说明j串比i串大了,同时j+m串也一定比i+m串大(m=1,2,3。。。。k)所以j到j+k之内是不会有最小串了。把j直接更新到j+k+1。大小顺序反过来的时候同理。最大表示法也同理。最后取i与j中小的一个就是其位置。另外注意一点就是当更新位置后i和j重叠的时候为方便编码同意j++。具体看代码。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000004
using namespace std;
char s[N];
int nexts[N];
int len,sum;
void getnext()
{
    int i=0,j=-1;
    nexts[0]=-1;
    while(i<len)
    {
        if(j==-1||s[i]==s[j])
        {
            i++;j++;
            if(s[i]==s[j])
                nexts[i]=nexts[j];
            else nexts[i]=j;
        }
        else j=nexts[j];
    }
}
int min_max(int flag)//这里把最小和最大表示法整合到一起,flag是0的话就是最小表示法
{
    int i=0,j=1,k=0;
    while(i<len&&j<len&&k<len)
    {
        int t=s[(i+k)%len]-s[(j+k)%len];
        if(t==0) k++;
        else if(!flag)
        {
            if(t<0) j=(j+k+1);
            else i=(i+k+1);
            if(i==j) j=(j+1);
            k=0;
        }
        else if(flag)
        {
            if(t<0) i=(i+k+1);
            else j=(j+k+1);
            if(i==j) j=(j+1);
            k=0;
        }
    }
    return min(i,j);
}
int main()
{
    while(~scanf("%s",s))
    {
        len=strlen(s);
        getnext();//getnext函数用来找循环节,循环群的知识告诉我们个数只能是字符串长度的约数
        int mins=min_max(0);
        int maxs=min_max(1);
        int l=len-nexts[len];
        sum=len%l?1:len/l;//求循环节
        printf("%d %d %d %d\n",mins+1,sum,maxs+1,sum);
    }
}


你可能感兴趣的:(最小最大表示法)