题意:
此题意很好理解,便不在此赘述;
题解:
解题思路:KMP求字符串最小循环节+拓展KMP
①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k;
②根据拓展KMP求出字符串s的nex[]数组,那么对于由第 i 位打头构成的新数b,如何判断其与原数a的大小关系呢?
1)如果 i%k == 0,那么b == a;
2)如果 i%k ≠ 0 ,令L=nex[i],那么只需判断s[ i+L ]与s[ L ]的大小关系即可,需要注意的是,如果i+L = len呢?此时又该怎样处理呢?
方法1:依次判断s[0,1,....] 与 s[ L,L+1,..... ]的关系,直到找出第一个不相等的位置判断其大小;
方法2:判断 s[ nex[L] ]与s[ L+nex[L] ]的大小关系;
如果采用方法1,很不幸,会超时,所以,方法2才是行之有效的方法;
根据题意,此题让求得是不同的数,那么,如何去重呢?
根据KMP已经求出了k,那么串s得循环周期为 len / k ,那么每种新数必然会重复 len / k次,只要在输出结果上将求出的答案除以 (len/k) 即可;
还有一点需要注意的是,和原数相同的数,当且仅当只有一个,不论输入任何数,输出1即可;
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1e6+50; 6 7 char digit[maxn]; 8 int nex[maxn]; 9 10 int Period() 11 { 12 int len=strlen(digit); 13 nex[0]=-1; 14 nex[1]=0; 15 int cnt=0; 16 int index=2; 17 while(index <= len) 18 { 19 if(digit[index-1] == digit[cnt]) 20 nex[index++]=++cnt; 21 else if(cnt != 0) 22 cnt=nex[cnt]; 23 else 24 nex[index++]=0; 25 } 26 int k=len; 27 if(len%(len-nex[len]) == 0 && nex[len] != 0) 28 k=len-nex[len]; 29 return k; 30 } 31 void getNext() 32 { 33 int len=strlen(digit); 34 nex[0]=len; 35 int j=0; 36 while(j+1 < len && digit[j+1] == digit[j]) 37 j++; 38 nex[1]=j; 39 int k=1; 40 for(int i=2;i < len;++i) 41 { 42 int p=k+nex[k]-1; 43 int l=nex[i-k]; 44 if(l < p-i+1) 45 nex[i]=l; 46 else 47 { 48 j=max(0,p-i+1); 49 while(i+j < len && digit[i+j] == digit[j]) 50 j++; 51 k=i; 52 nex[i]=j; 53 } 54 } 55 } 56 bool isLess(int i,int j,int len) 57 { 58 if(j == len)//如果j == len 59 { 60 j=nex[i]; 61 i=i+nex[i]; 62 } 63 return (digit[j]-'0') < (digit[i]-'0'); 64 } 65 void Solve() 66 { 67 int k=Period();//KMP求出最小循环节的长度 68 getNext();//拓展KMP求解nex[] 69 70 int ansL=0; 71 int ansG=0; 72 int len=strlen(digit); 73 for(int i=1;i < len;++i) 74 { 75 int l=nex[i]; 76 if(i%k == 0)//与原数相等 77 continue; 78 79 if(isLess(l,i+l,len))//判断是否小于原数 80 ansL++; 81 else 82 ansG++; 83 } 84 printf(" %d %d %d\n",ansL/(len/k),1,ansG/(len/k)); 85 } 86 int main() 87 { 88 int test; 89 scanf("%d",&test); 90 for(int kase=1;kase <= test;++kase) 91 { 92 scanf("%s",digit); 93 printf("Case %d:",kase); 94 Solve(); 95 } 96 return 0; 97 }
在网上看的其他人写的题解,有个很巧妙的方法:
将字符串s拷贝一份加入到字符串s中,通过拓展KMP求出nex[]后,对于由第 i 打头构成的新数b:
1)如果nex[i] > len/2,那么b == a;
2)判断s[ i+nex[i] ]与s[ nex[i] ]的相对大小;