【扩展kmp+最小循环节】HDU 4333 Revolving Digits
http://acm.hdu.edu.cn/showproblem.php?pid=4333
【题意】
- 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的数比原数小的个数,相等的个数,大的个数
【思路】
- 这个数很大,用字符串处理
- 比较两个字符串的大小,一位一位很耗时,可以求出最长公共前缀,只比较最长公共前缀后一位
- 每次将数的最后一位放到最后一位,如abcd变成dabc,cdab,bcda,相当于abcdabcd各个后缀的前四位
- 这样就变成了求abcdabcd的每个后缀与abcd的最长公共前缀,用扩展KMP线性求
- abab这种情况按上面的做法abababab会考察ab,ba,ab,ba,有重复的,是因为abab=(ab)^2,所以还要考虑最小循环节去重
【AC】
1 #include<iostream> 2 #include<cstring> 3 #include<string> 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 typedef long long ll; 8 const int maxn=2e5+3; 9 char s[maxn]; 10 char t[maxn]; 11 int nxt[maxn]; 12 int extend[maxn]; 13 int nxtval[maxn]; 14 15 16 void pre_EKMP(char x[],int m,int nxt[]) 17 { 18 nxt[0]=m; 19 int j=0; 20 while(j+1<m && x[j]==x[j+1]) j++; 21 nxt[1]=j; 22 int k=1; 23 for(int i=2;i<m;i++) 24 { 25 int p=nxt[k]+k-1; 26 int L=nxt[i-k]; 27 if(i+L<p+1) nxt[i]=L; 28 else 29 { 30 j=max(0,p-i+1); 31 while(i+j<m && x[i+j]==x[j]) j++; 32 nxt[i]=j; 33 k=i; 34 } 35 } 36 } 37 38 void EKMP(char x[],int m,char y[],int n,int nxt[],int extend[]) 39 { 40 pre_EKMP(x,m,nxt);//子串 41 int j=0; 42 while(j<n && j<m &&x[j]==y[j]) j++; 43 extend[0]=j; 44 int k=0; 45 for(int i=1;i<n;i++) 46 { 47 int p=extend[k]+k-1; 48 int L=nxt[i-k]; 49 if(i+L<p+1) extend[i]=L; 50 else 51 { 52 j=max(0,p-i+1); 53 while(i+j<n && j<m && y[i+j]==x[j]) j++; 54 extend[i]=j; 55 k=i; 56 } 57 } 58 } 59 60 void kmp_pre(char x[],int m,int nxtval[]) 61 { 62 int i,j; 63 j=nxtval[0]=-1; 64 i=0; 65 while(i<m) 66 { 67 if(j==-1||x[i]==x[j]) 68 { 69 i++; 70 j++; 71 if(x[i]!=x[j]) nxtval[i]=j; 72 else nxtval[i]=nxtval[j]; 73 } 74 else j=nxtval[j]; 75 } 76 77 } 78 79 void NextVal(char *T) 80 { 81 int i=0,j=-1; 82 nxtval[0]=-1; 83 int Tlen=strlen(T); 84 while(i<Tlen) 85 { 86 if(j==-1||T[i]==T[j]) 87 { 88 i++; 89 j++; 90 if(T[i]!=T[j]) nxtval[i]=j; 91 else nxtval[i]=nxtval[j]; 92 } 93 else j=nxtval[j]; 94 } 95 } 96 97 98 int main() 99 { 100 int T; 101 scanf("%d",&T); 102 int cas=0; 103 while(T--) 104 { 105 scanf("%s",s); 106 strcpy(t,s); 107 strcat(s,t); 108 int a,b,c; 109 a=b=c=0; 110 int ls=strlen(s); 111 int lt=strlen(t); 112 EKMP(t,lt,s,ls,nxt,extend); 113 kmp_pre(t,lt,nxtval); 114 int p=lt-nxtval[lt]; 115 int tmp=1; 116 if(lt%p==0) tmp=lt/p; 117 for(int i=0;i<lt;i++) 118 { 119 if(extend[i]==lt) a++; 120 else if(s[i+extend[i]]>t[extend[i]]) c++; 121 else b++; 122 } 123 printf("Case %d: %d %d %d\n",++cas,b/tmp,a/tmp,c/tmp); 124 } 125 return 0; 126 }
扩展kmp计算最小循环节方法二:利用已知的next数组
【知识点】
扩展kmp的next数组与kmp数组的next含义不同,是字符串s的所有后缀和s本身的最长公共前缀
【坑点】
做这道题踩了各种坑
- strcat函数的用法:strcat(s,s)是错误的,会T,strcat的两个参数传的是指针,就是s在内存里面的位置,这里两个s是同一个东西 第一个s变长的时候,第二个s也会变长,然后就没完没了了
-