HDU-3374 String Problem (最小表示法)

题意:给你一个字符串,并让他不断的进行循环左移,问字典序最小的字符串是第几个,出现的次数是多少,最大的字符串是第几个,出现的次数是多少?

 

分析:最小字符串和最大字符串用最小表示法即可找到,出现的次数,就是循环节循环的次数,用KMP的Nextd的数组得到

 

代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000002;
int Next[N];
char S[N];
string T;
int tlen,loop_len,loop_num;
string maxxstr;
string minnstr;
int maxx_id;
int minn_id;
int MINR(string s,int l){        //s是原串(未加倍过),l是原串长
    for(int i=0;i<l;++i)s+=s[i];    //将s串加倍

    int i=0,j=1;                    //利用i,j指针移动
    while(i<l&&j<l){
        int k=0;
        while(s[i+k]==s[j+k]&&k<l)++k;    //不断比较直到比较完长度为l的串或两个子串不相等
        if(k==l)return min(i,j);        //若比较出长度为l则直接返回靠前的那个串的开始位置
        if(s[i+k]>s[j+k])i=max(i+k+1,j+1);    //i串比j串大,那么i到i+k中的串都比j串大,i可以直接移动到i+k+1位置,而起始位置比j小的肯定都在j移动过程中比较过,所以i可以直接移动到j+1位置,因此取这两值的最大值
        else j=max(j+k+1,i+1);    //同上
    }
    return min(i,j);            //返回位置靠前的下标
}

int MAXR(string s,int l){
    for(int i=0;i<l;++i)s+=s[i];

    int i=0,j=1;
    while(i<l&&j<l){
        int k=0;
        while(s[i+k]==s[j+k]&&k<l)++k;
        if(k==l)return min(i,j);
        if(s[i+k]<s[j+k])i=max(i+k+1,j+1);
        else j=max(j+k+1,i+1);
    }
    return min(i,j);
}
void getNext()
{
    int j, k;
    j = 0; k = -1; Next[0] = -1;
    while(j < tlen)
        if(k == -1 || T[j] == T[k])
            Next[++j] = ++k;
        else
            k = Next[k];

}
int main()
{

      while(scanf("%s",S)!=EOF)
      {
        T=S;
      maxxstr="";
      minnstr="";
      tlen=T.size();
      getNext();
      loop_len=tlen-Next[tlen];
      if(tlen%loop_len!=0)
     {
      loop_num=1;
      loop_len=tlen;
     }
      else
      loop_num=tlen/(loop_len);
      minn_id=MINR(T,tlen)+1;
      maxx_id=MAXR(T,tlen)+1;
      cout<<minn_id<<" "<<loop_num<<" "<<maxx_id<<" "<<loop_num<<endl;
      }
    return 0;
}

 

posted @ 2018-09-30 10:37  hinata_hajime  阅读(173)  评论(0编辑  收藏  举报