hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)

传送门

 

题意:

  此题意很好理解,便不在此赘述;

题解:

  解题思路: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 }
View Code

  在网上看的其他人写的题解,有个很巧妙的方法:

  将字符串s拷贝一份加入到字符串s中,通过拓展KMP求出nex[]后,对于由第 i 打头构成的新数b:

  1)如果nex[i] > len/2,那么b == a;

  2)判断s[ i+nex[i] ]与s[ nex[i] ]的相对大小;

posted @ 2019-03-15 20:14 hyacinthLJP 阅读(...) 评论(...) 编辑 收藏