题意:给你一个数,每次把这个数尾巴上的一个数字放到前面来,问如此循环一遍形成的新的(不重复)数字中,大于,等于,小于原数字的数各有多少个。
比如样例:341->134->413->341,小于、等于、大于的各有1个。
这个串后面接上它本身,作为主串,原串作为模式串。显然这题就是要求出主串每个后缀与模式串的最长公共前缀,直接套扩展KMP模板即可。
因为形成的新的数字必须不重复,因此还需要用KMP的next函数求一下最短循环节。
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 2001000; int next[MAXN],extand[MAXN]; char S[MAXN],T[MAXN]; int nextval[MAXN]; int len; void KMPgetNext(char s[],int next[]) { int length=len; int i=0,j=-1; next[0]=-1; while(i<length) { if(j==-1||s[i]==s[j]) { ++i; ++j; next[i]=j; } else j=next[j]; } return; } void GetNext(const char *T) { len=strlen(T); int a=0; next[0]=len; while(a<len-1 && T[a]==T[a+1]) a++; next[1]=a; a=1; for(int k=2; k<len; k++) { int p=a+next[a]-1,L=next[k-a]; if( (k-1)+L >= p) { int j = (p-k+1)>0 ? (p-k+1) : 0; while(k+j<len && T[k+j]==T[j]) j++; next[k]=j; a=k; } else next[k]=L; } } void GetExtand(const char *S,const char *T) { GetNext(T); int slen=strlen(S),tlen=strlen(T),a=0; int MinLen = slen < tlen ? slen : tlen; while(a<MinLen && S[a]==T[a]) a++; extand[0]=a; a=0; for(int k=1; k<slen; k++) { int p=a+extand[a]-1, L=next[k-a]; if( (k-1)+L >= p) { int j= (p-k+1) > 0 ? (p-k+1) : 0; while(k+j<slen && j<tlen && S[k+j]==T[j]) j++; extand[k]=j; a=k; } else extand[k]=L; } } int main() { int casN, cas = 0; scanf( "%d", &casN ); while ( casN-- ) { scanf( "%s", T ); strcpy( S, T ); strcat( S, T ); GetExtand( S, T ); KMPgetNext( T, nextval ); int subL = ( len % ( len - nextval[len]) ) ? len : ( len - nextval[len] ); //printf("%d ", subL ); int L = 0, E = 0, G = 0; for ( int i = 0; i < subL; ++i ) { if ( extand[i] >= len ) ++E; else if ( S[ extand[i] ] > S[ i + extand[i] ] ) ++L; else if ( S[ extand[i] ] < S[ i + extand[i] ] ) ++G; } printf("Case %d: %d %d %d ", ++cas, L, E, G ); } return 0; }