HDU - 5442Favorite Donut 最大表示法+KMP/后缀数组
Posted lqllulu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 5442Favorite Donut 最大表示法+KMP/后缀数组相关的知识,希望对你有一定的参考价值。
题意
给出一个长度为n的环状由小写字母组成的序列,请找出从何处断开,顺时针还是逆时针,使得字典序最大。如果两个字符串的字典序一样大,那么它会选择下下标最小的那个。如果某个点顺时针逆时针产生的字典序大小相同,那么优先选择顺时针的。
这个题用最大表示法+KMP很容易解决。因为最大表示法找到的是下表最小的那个字典序最大的字符串,所以正向的时候最大表示法找出来的就直接是答案,关键是逆时针的时候。我们将字符串翻转以后用最大表示法找到那个字符串s2,然后用KMP算法在翻转*2后的串中找出最后面的那个s2,这就是逆时针时候的答案。然后比较顺时针和逆时针时候的答案,选取最优的就可以了。
下面是代码。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 7 using namespace std; 8 const int maxn=40000+100; 9 char s[maxn],str[maxn]; 10 char s1[maxn],s2[maxn]; 11 int T,n,ans1,ans2; 12 int get_max(char *S){ 13 int len=strlen(S); 14 int i=0,j=1,k=0; 15 while(i<len&&j<len&&k<len){ 16 int t=S[(i+k)%len]-S[(j+k)%len]; 17 if(t==0)k++; 18 else{ 19 if(t>0) 20 j+=k+1; 21 else 22 i+=k+1; 23 if(i==j)j++; 24 k=0; 25 } 26 } 27 return min(i,j); 28 } 29 int f[maxn]; 30 void get_fail(char *P){ 31 int n=strlen(P); 32 f[0]=0,f[1]=0; 33 for(int i=1;i<n;i++){ 34 int j=f[i]; 35 while(j&&P[i]!=P[j])j=f[j]; 36 f[i+1]=P[i]==P[j]?j+1:0; 37 } 38 } 39 int find(char *T,char *P){ 40 int res=0,len=strlen(T),m=strlen(P); 41 get_fail(P); 42 int j=0; 43 for(int i=0;i<2*n;i++){ 44 while(T[i]!=P[j]&&j)j=f[j]; 45 if(T[i]==P[j])j++; 46 if(j==m){ 47 if(i-m+1<n){ 48 res=i-m+1; 49 } 50 } 51 } 52 return res; 53 } 54 int main(){ 55 scanf("%d",&T); 56 for(int t=1;t<=T;t++){ 57 scanf("%d",&n); 58 scanf("%s",str); 59 ans1=get_max(str); 60 for(int i=0;i<n;i++){ 61 s1[i]=str[(ans1+i)%n]; 62 } 63 s1[n]=0; 64 reverse(str,str+n); 65 int j=get_max(str); 66 for(int i=0;i<n;i++){ 67 s2[i]=str[(j+i)%n]; 68 } 69 s2[n]=0; 70 for(int i=0;i<n;i++) 71 str[i+n]=str[i]; 72 str[2*n]=0; 73 int ans2=find(str,s2); 74 int ok=0; 75 ans2=n-ans2-1; 76 // printf("%d %d ",ans1,ans2); 77 // printf("%s %s ",s1,s2); 78 79 for(int i=0;i<n;i++){ 80 if(s1[i]>s2[i]){ 81 ok=1; 82 break; 83 } 84 if(s1[i]<s2[i]){ 85 ok=-1; 86 break; 87 } 88 } 89 ans1++,ans2++; 90 if(ok>0){ 91 printf("%d %d ",ans1,0); 92 } 93 else if(ok<0){ 94 printf("%d %d ",ans2,1); 95 } 96 else{ 97 if(ans2<ans1){ 98 printf("%d %d ",ans2,1); 99 }else if(ans1<ans2){ 100 printf("%d %d ",ans1,0); 101 }else{ 102 printf("%d %d ",ans1,0); 103 } 104 } 105 } 106 return 0; 107 }
这个题显然也可以用后缀数组来搞。跟上面的思路差不多,顺时针的时候可以直接用sa数组找出答案(因为前面的一定长于后面的),关键是逆时针的时候如何找出下标最大的最大字典序的字符串。我们可以利用一下height数组。当height[i]==len的时候,就是我们要的答案。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=60000+10; 8 int T,n; 9 char s[maxn]; 10 char s1[maxn],s2[maxn]; 11 int sa[maxn],c[maxn],x[maxn],y[maxn],height[maxn],rak[maxn]; 12 void build_sa(int m){ 13 memset(y,-1,sizeof(y)); 14 for(int i=0;i<m;i++)c[i]=0; 15 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 16 for(int i=1;i<m;i++)c[i]+=c[i-1]; 17 for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 18 19 for(int k=1;k<=n;k<<=1){ 20 int p=0; 21 for(int i=n-k;i<n;i++)y[p++]=i; 22 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 23 24 for(int i=0;i<m;i++)c[i]=0; 25 for(int i=0;i<n;i++)c[x[y[i]]]++; 26 for(int i=1;i<m;i++)c[i]+=c[i-1]; 27 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 28 swap(x,y); 29 p=1,x[sa[0]]=0; 30 for(int i=1;i<n;i++) 31 x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++; 32 if(p>=n)break; 33 m=p; 34 } 35 } 36 37 void getHeight(){ 38 int k=0; 39 for(int i=0;i<n;i++)rak[sa[i]]=i; 40 for(int i=0;i<n;i++){ 41 if(k)k--; 42 int j=sa[rak[i]-1]; 43 while(s[i+k]==s[j+k])k++; 44 height[rak[i]]=k; 45 } 46 } 47 int R[maxn],num; 48 char str[maxn]; 49 int ans1,ans2,len; 50 int main(){ 51 scanf("%d",&T); 52 for(int t=1;t<=T;t++){ 53 scanf("%d",&len); 54 scanf("%s",str); 55 for(int i=0;i<len;i++){ 56 s[i]=str[i]; 57 s[i+len]=str[i]; 58 } 59 n=2*len; 60 s[n]=0; 61 build_sa(123); 62 for(int i=n-1;i>=0;i--){ 63 if(sa[i]<len){ 64 ans1=sa[i]; 65 break; 66 } 67 } 68 for(int i=0;i<len;i++) 69 s1[i]=s[i+ans1]; 70 reverse(str,str+len); 71 for(int i=0;i<len;i++){ 72 s[i]=str[i]; 73 s[i+len]=str[i]; 74 } 75 s[n]=0; 76 build_sa(123); 77 getHeight(); 78 // printf("%s ",s); 79 // for(int i=0;i<n;i++) 80 // printf("%d ",sa[i]); 81 // printf(" "); 82 // for(int i=0;i<n;i++) 83 // printf("%d ",height[i]); 84 // printf(" "); 85 for(int i=n-1;i>=0;i--){ 86 if(sa[i]<len){ 87 ans2=sa[i]; 88 if(height[i]<=len) 89 break; 90 } 91 } 92 for(int i=0;i<len;i++){ 93 s2[i]=s[i+ans2]; 94 } 95 s2[len]=0; 96 int ok=0; 97 for(int i=0;i<len;i++){ 98 if(s1[i]>s2[i]){ 99 ok=1; 100 break; 101 } 102 if(s1[i]<s2[i]){ 103 ok=-1; 104 break; 105 } 106 } 107 ans2=len-ans2-1; 108 ans1++,ans2++; 109 //printf("%d %d %d ",ans1,ans2,ok); 110 if(ok>0){ 111 printf("%d 0 ",ans1); 112 }else if(ok<0){ 113 printf("%d 1 ",ans2); 114 }else{ 115 if(ans2<ans1){ 116 printf("%d 1 ",ans2); 117 }else{ 118
以上是关于HDU - 5442Favorite Donut 最大表示法+KMP/后缀数组的主要内容,如果未能解决你的问题,请参考以下文章