HDU 4333 Revolving Digits [扩展KMP]

  其实是很裸的一道题,可能是因为几乎没用过扩展KMP,只想到用后缀数组,但这题的常数卡的很厉害,不用扩展KMP应该是过不了的。

  扩展KMP能求出一个串所有后缀串(即s[i...len])和模式串的最长公共前缀。于是只要将这个串复制一遍,求出该串每个后缀与其本身的最长公共前缀即可,当公共前缀>=len时,显然相等,否则只要比较下一位就能确定这个串与原串的大小关系。

  至于重复串的问题,只有当这个串有循环节的时候才会产生重复串,用KMP的next数组求出最小循环节,用长度除以最小循环节得到循环节个数,在将3个答案都除以循环节个数即可。

  

 1 #include <string.h>
 2 #include <stdio.h>
 3 #define MAXN 2000005
 4 int cas;
 5 char s[MAXN],s2[MAXN];
 6 int next[MAXN],ext[MAXN];
 7 int max(int x,int y){return x>y?x:y;}
 8 int len;
 9 int ExKMP(char *t,char *s){
10     int lent=len,lens=len*2,ans=0;
11     next[1]=lent;
12     int j=1,k=2;
13     while(j<lent&&t[j]==t[j+1])++j;
14     next[2]=j-1;
15     for(int i=3;i<=lent;i++){
16         if(next[i-k+1]<k+next[k]-i)next[i]=next[i-k+1];
17         else{
18             j=max(k+next[k]-i,0);
19             while(i+j<=lent&&t[i+j]==t[j+1])++j;
20             next[i]=j,k=i;
21         }
22     }
23     j=1,k=1;
24     while(j<=lent&&j<=lens&&s[j]==t[j])++j;
25     ext[1]=j-1;
26     if(ext[1]==lent)ans++;
27     for(int i=2;i<=lens;i++){
28         if(next[i-k+1]<k+ext[k]-i)ext[i]=next[i-k+1];
29         else{
30             j=max(k+ext[k]-i,0);
31             while(i+j<=lens&&j<lent&&s[i+j]==t[j+1])++j;
32             ext[i]=j,k=i;
33         }
34         if(ext[i]==lent)ans++;
35     }
36     return ans;
37 }
38 int main(){
39     //freopen("test.in","r",stdin);
40     scanf("%d",&cas);
41     for(int ca=1;ca<=cas;ca++){
42         scanf("%s",s+1);
43         len=strlen(s+1);
44         for(int i=1;i<=len;i++)s2[i]=s2[i+len]=s[i];
45         s2[len*2+1]='\0';
46         ExKMP(s,s2);
47         int x1=0,x3=0;
48         for(int i=1;i<=len;i++){
49             if(ext[i]<len){
50                 if(s2[i+ext[i]]<s[ext[i]+1])x1++;
51                 else x3++;
52             }
53         }
54         next[1]=0;
55         for(int i=2,j=0;i<=len;i++){
56             while(j>0&&s[i]!=s[j+1])j=next[j];
57             if(s[i]==s[j+1])++j;
58             next[i]=j;
59         }
60         int tot=1;
61         if(len%(len-next[len])==0)tot=len/(len-next[len]);
62         printf("Case %d: %d %d %d\n",ca,x1/tot,1,x3/tot);
63     }
64     return 0;
65 }

  

原文地址:https://www.cnblogs.com/swm8023/p/2661264.html