[USACO07DEC]Best Cow Line Gc++
小聲嗶嗶:字符串hash牛逼數組
給出一個字符串,每次能夠從字符串的首尾取出一個字符,放到隊列的尾部,求能夠獲得的最小的字典序是多少?spa
此時字符串首尾的下標分別爲l,r。code
若是str[l]!=str[r]
:取較小的字符串隊列
若是str[l]==str[r]
:找到第一個非負整數x,使得str[l+x]!=str[r-x]
。字符串
若是str[l+x]<str[r-x]
,那麼此時取str[l]
,不然取str[r]
;get
數據範圍是\(1 \leq N \leq 5\times10^5\),若是暴力複雜度比較高博客
若是快速找到x是關鍵,我想了字符串hash,沒想到二分判斷條件。(我好菜啊啊啊啊啊啊啊)
對於l,r,咱們二分x的值,找到第一個hash值不想等的x。string
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int N=1e6+10; const int mod=1e9+7; const int inf=0x3f3f3f3f; char str[N]; string ans; ull bin[N],hash1[N],hash2[N]; int n; void init() { bin[0]=1; for(int i=1; i<=n; i++) { bin[i]=bin[i-1]*137; hash1[i]=hash1[i-1]*137+str[i]-'a'+1; hash2[i]=hash2[i-1]*137+str[n-i+1]-'a'+1; } } ull get1(int l,int r) { return hash1[r]-hash1[l-1]*bin[r-l+1]; } ull get2(int l,int r) { return hash2[r]-hash2[l-1]*bin[r-l+1]; } int solve(int aga,int en) { int l=0,r=(en-aga)/2,ans=0; while(l<=r) { int mid=(l+r)/2; if(get1(aga,aga+mid)!=get2(n+1-en,n+1-en+mid)) { ans=mid; r=mid-1; } else l=mid+1; } return ans; } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { getchar(); scanf("%c",&str[i]); } init(); int l=1,r=n; while(l<=r) { if(l==r) { ans+=str[l]; break; } if(str[l]<str[r]) ans+=str[l++]; else if(str[l]>str[r]) ans+=str[r--]; else { int len=solve(l,r); if(str[l+len]<str[r-len]) ans+=str[l++]; else ans+=str[r--]; } } for(int i=0; i<ans.size(); i++) { printf("%c",ans[i]); if((i+1)%80==0) printf("\n"); } return 0; }
pre[i]
表示以i開頭的前綴(即把以i結尾的前綴倒過來)hash
suf[i]
表示以i開頭的後綴
當str[l]==str[r]
的時候,咱們只須要比較pre[r]
和suf[l]
的排名
對於pre[r]
,咱們在結尾加一個字符,而後把原串反過來添加到末尾,求後綴數組,pre[r]
就是suf[n+n-r+2]
。
好比acabca,處理完就是acabca#acbaca
比較兩個c的時候,其實就是比較第一個c的排名和倒數第二個c的排名
#include <bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int N = 1e6 + 10; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; char str[N]; string ans; int n, m, sa[N], rk[N], oldrk[N<<1], pos[N], cnt[N]; bool cmp(int x, int y, int k) { return oldrk[x] == oldrk[y] && oldrk[x + k] == oldrk[y + k]; } void getsa() { m = 122; for (int i = 1; i <= n; i++) ++cnt[rk[i] = str[i]]; for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1]; for (int i = n; i; i--) sa[cnt[rk[i]]--] = i; for (int k = 1; k <= n; k <<= 1) { int num = 0; for (int i = n - k + 1; i <= n; i++) pos[++num] = i; for (int i = 1; i <= n; i++) { if (sa[i] > k) pos[++num] = sa[i] - k; } memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) ++cnt[rk[i]]; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for (int i = n; i; i--) sa[cnt[rk[pos[i]]]--] = pos[i]; num = 0; memcpy(oldrk, rk, sizeof(rk)); for (int i = 1; i <= n; i++) rk[sa[i]]=cmp(sa[i],sa[i-1],k)?num:++num; if(num==n) break; m=num; } for(int i=1;i<=n;i++) rk[sa[i]]=i; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { getchar(); scanf("%c",&str[i]); } str[n+1]='0'; for(int i=n+2;i<=n*2+1;i++) str[i]=str[2*n-i+2]; n=n*2+1; getsa(); int l=1,r=n/2; while(l<=r) { if(str[l]<str[r]) ans+=str[l++]; else if(str[l]>str[r]) ans+=str[r--]; else { if(rk[l]<rk[n/2+n/2-r+2]) ans+=str[l++]; else ans+=str[r--]; } } for(int i=0;i<ans.size();i++) { printf("%c",ans[i]); if((i+1)%80==0) printf("\n"); } return 0; } /* 6 a c a b c b */