bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换
题意:
有一个奶牛队列。每次可以在原来队列的首端或是尾端牵出一头奶牛,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有奶牛都被插到了新的队列里。这样得到的队列,就是FJ拉去登记的最终的奶牛队列。 求对于给定的奶牛们的初始位置,计算出可能得到的字典序最小的队列。队列大小≤30000。
题解:
有一个结论:如果当前队列中的队首元素不等于队尾元素,则选小的那一边;否则选以右端点为下标的前缀和以左端点为下标的后缀中较小的那边。对于比较前缀后缀,正解是后缀数组,然而本弱偷懒写了个哈希,比较两个字符串的大小只要二分LCP,然后比较LCP的下一个字符即可。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 30010 7 #define ll unsigned long long 8 #define num 233 9 using namespace std; 10 11 inline int read(){ 12 char ch=getchar(); int f=1,x=0; 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 14 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 15 return f*x; 16 } 17 ll hash1[maxn],hash2[maxn],mi[maxn]; int l,r,n,tot; char name1[maxn],name2[maxn],ans[maxn]; 18 bool cmp(int x,int y){ 19 int l=1,r=min(n-x+1,y),z=0; 20 while(l<=r){ 21 int mid=(l+r)>>1; 22 if(hash1[x+mid-1]-hash1[x-1]*mi[mid]==hash2[n-y+1+mid-1]-hash2[(n-y+1)-1]*mi[mid])z=mid,l=mid+1;else r=mid-1; 23 } 24 return name1[x+z]<=name2[n-y+1+z]; 25 } 26 int main(){ 27 n=read(); inc(i,1,n)scanf("%s",name1+i); inc(i,1,n)name2[i]=name1[n-i+1]; mi[0]=1; 28 inc(i,1,n)hash1[i]=hash1[i-1]*num+name1[i],hash2[i]=hash2[i-1]*num+name2[i],mi[i]=mi[i-1]*num; 29 l=1; r=n; 30 while(l<r){ 31 if(name1[l]<name1[r])ans[++tot]=name1[l],l++; 32 else if(name1[l]>name1[r])ans[++tot]=name1[r],r--; 33 else if(cmp(l,r))ans[++tot]=name1[l],l++; 34 else ans[++tot]=name1[r],r--; 35 } 36 ans[++tot]=name1[l]; inc(i,1,tot){printf("%c",ans[i]); if(i%80==0)puts("");} return 0; 37 }
20161102