HDU 3374 String Problem(最大最小表示法+kmp)

Time Limit:1000MS Memory Limit:32768KB

Description
Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.

Input
Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters.

Output
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.

Sample Input
abcder
aaaaaa
ababab

Sample Output
1 1 6 1
1 6 1 6
1 3 2 3

题意: 给你一个原始的字符串,每次都将字符串的第一个字母放到最后一位,形成新的字符串,然后求出这些字符串中最小的和最大的出现的次数。

最大最小表示法:
给你一个字符串,然后每次将第一个字母放到最后一位,形成新的字符串
然后求这些字符串中最小的和最大的出现的次数
最大最小表示法是指用两个指针来指向原来的字符串,然后将整个字符串扫一遍

方法:
将p1初始化为0,p2初始化为1,k初始化为0

求最大的字符串的时候:
如果s[p1+k]小于s[p2+k],p1=p1+k+1,如果变化之后p1==p2,那么p1继续加一,k置为零
如果s[p1+k]>s[p2+k],p2=p2+k+1,如果变化之后p1==p2,那么p2继续加一,k置为零
如果s[p1+k]==s[p2+k],k++;
最后返回p1和p2中较小的那一个

求最小的字符串的时候:
如果s[p1+k]小于s[p2+k],p2=p2+k+1,如果变化之后p1==p2,那么p2继续加一,k置为零
如果s[p1+k]>s[p2+k],p1=p1+k+1,如果变化之后p1==p2,那么p1继续加一,k置为零
如果s[p1+k]==s[p2+k],k++;
最后返回依然是返回p1和p2中较小的一个

kmp:这里主要是用kmp里面的next数组来求最小循环节,一开始我还傻傻地枚举了所有的串,然后很喜闻乐见的超时了,后来才发现,哦~原来,next数组还可以用来求最小循环节!嗯,又涨姿势了。

思路: 求最小和最大的字符串用的是最大最小表示法,然后求次数就是用next数组来求最小循环节。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define maxn 1000010
#define PI acos(-1.0)    //圆周率
const int mod=1e4+7;
using namespace std;
char s[maxn];
int f[maxn];
void makenext(char *a)
{
    int len=strlen(a);
    int i=0;
    int j=-1;
    f[0]=-1;
    while(iif(j==-1||a[i]==a[j])
        {
            i++;
            j++;
            f[i]=j;
        }
        else  j=f[j];
    }
//    for(int i=0;i<=len;i++)  cout<
}
int makebig(char *a)
{
    int len=strlen(a);
    int p1=0;
    int p2=1;
    int k=0;
    while(kint flag=a[(p1+k)%len]-a[(p2+k)%len];
        if(flag==0)  k++;
        else
        {
            if(flag<0)               //如果p1+k指向的字母比p2+k指向的字母小,p1向后移动
            {
                p1+=k+1;
                if(p1==p2)  p1++;    //如果p1移动后与p2相等,继续往后移一位
            }
            else                     //如果p1+k指向的字母比p2+k指向的字母大,p2向后移动
            {
                p2+=k+1;
                if(p1==p2)  p2++;    //如果p2移动后与p1相等,继续往后移一位
            }
            k=0;
        }
    }
    return min(p1,p2);
}
int makesmall(char *a)               //求最小的和求最大的同理
{
    int len=strlen(a);
    int p1=0;
    int p2=1;
    int k=0;
    while(kint flag=a[(p1+k)%len]-a[(p2+k)%len];
        if(flag==0)  k++;
        else
        {
            if(flag<0)
            {
                p2+=k+1;
                if(p1==p2)  p2++;
            }
            else
            {
                p1+=k+1;
                if(p1==p2)  p1++;
            }
            k=0;
        }
    }
    return min(p1,p2);
}
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        int len=strlen(s);
        makenext(s);

        int big=makebig(s)+1;
        int small=makesmall(s)+1;

   //     cout<<"big: "<
   //     cout<<"small: "<

        int wei=0;
        if(len%(len-f[len]))  wei=1;
        else  wei=len/(len-f[len]);        //用next数组求出这个字符串的最小循环节

        printf("%d %d %d %d\n",small,wei,big,wei);
    }

    return 0;
}

你可能感兴趣的:(字符串)