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; }