题意:给你一个字符串s,找到满足条件(s[i]的下一个字符是s[(i*i+1)%n])的最大字典序的长度为n的串。
思路:类似后缀数组,每次倍增来对以i开头的字符串排序,复杂度O(nlogn)。代码很多地方借鉴后缀数组。
倍增:比如这次排序好了长度为m的串,那么想扩展为长度为2*m的串则需要用i的排名为第一关键字,i+m的排名为第二关键字进行排序,这种排序正好可以使用基数排序。
下面是代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=155555; 6 int n; 7 int m; 8 int tp[maxn],Rank[maxn],SA[maxn],tax[maxn]; 9 void Rsort()///SA为排名为i的元素所在的位置,tp为第二关键字,Rank作为第一关键字,Rank为i元素的排名。 10 { 11 for(int i=0; i<=m; i++)tax[i]=0; 12 for(int i=1; i<=n; i++)tax[Rank[tp[i]]]++; 13 for(int i=1; i<=m; i++)tax[i]+=tax[i-1]; 14 for(int i=n; i>=1; i--)SA[tax[Rank[tp[i]]]--]=tp[i]; 15 } 16 char st[maxn]; 17 int go[maxn][2]; 18 int main() 19 { 20 int t; 21 while(~scanf("%d",&t)) 22 { 23 int c=1; 24 while(t--) 25 { 26 scanf("%d",&n); 27 scanf("%s",st+1); 28 for(long long i=1; i<=n; i++) 29 go[i][0]=((i-1)*(i-1)+1)%n+1;///滚动数组,倍增查找i后面的第2^k的位置。 30 m=11; 31 for(int i=1; i<=n; i++) 32 tp[i]=i,Rank[i]=st[i]-'0'; 33 int p; 34 for(int t=0; t<18&&(1<<t)<=n; t++) 35 { 36 for(int i=0;i<=m;i++)tax[i]=0; 37 for(int i=1; i<=n; i++)tax[Rank[go[i][t&1]]]++; 38 for(int i=1;i<=m;i++)tax[i]+=tax[i-1]; 39 for(int i=1;i<=n;i++)tp[tax[Rank[go[i][t&1]]]--]=i; 40 Rsort(); 41 swap(tp,Rank); 42 Rank[SA[1]]=p=1; 43 for(int i=2; i<=n; i++)Rank[SA[i]]=(tp[SA[i]]==tp[SA[i-1]]&&tp[go[SA[i]][t&1]]==tp[go[SA[i-1]][t&1]])?p:++p; 44 if(Rank[SA[n]]!=Rank[SA[n-1]])break;///剪枝,如果已经能确定最大的了 直接break 45 m=p; 46 for(int i=1;i<=n;i++) 47 go[i][!(t&1)]=go[go[i][t&1]][t&1];///这里是倍增,go[i]代表i后面第2^t的元素的位置 48 } 49 printf("Case #%d: ",c++); 50 long long loc=SA[n]-1; 51 for(int i=0; i<n; i++,loc=(loc*loc+1)%n) 52 printf("%c",st[loc+1]); 53 puts(""); 54 } 55 } 56 return 0; 57 }