洛谷P6140&P2870 [USACO07NOV]Best Cow Line S
阿新 • • 發佈:2020-12-29
題意
給定一個字串,每次珂以取出當前字串的頭或者尾,將其加入答案串中(放到最後面),要求必須把該字串取完,求字典序最小的方案
分析
SA簡單題
題目要求的是求字典序最小,很容易想到SA來求
我們觀察一下,對於每一位可能的情況就只有兩種,所以我們想到貪心地取,每次取當前頭和尾當中較小的那個字元
但是這樣會出現一個問題:如果頭和尾相同該怎麼辦?
比如這樣的一個字串:BCAB
現在我們取這個串就不知道該取頭還是尾,這個時候如果我們貿然取頭的話(變成CAB),我們下一步只能取到C或B(當前就應該取B)
這樣幹之後我們會取到BB作為當前已經取了的,實際上我們有更優的選擇,那就是第一次取的時候取尾,然後我們珂以取到BA這個串,它的字典序更小
那麼我們考慮在這種情況下怎麼搞。
珂以發現,其實每次取都是在檢視當前狀態下,是當前串的字典序小,還是當前反串的字典序小,那麼這就相當於是比較兩個字串的字典序誰大誰小的問題
很容易想到字尾陣列SA來做。
具體就是設原串為A,設A的反串是~A,那麼此時我們考慮拼接這樣的一個字串 B=A+(分隔符)+ ~A
此時我們對B串跑一邊SA就可以得到原串和反串字典序排序之後的相對大小了(rk陣列)
每次選字元的時候\(O(1)\)比較一下就行了
程式碼
#include<bits/stdc++.h> using namespace std; template <typename T> inline void read(T &x){ x=0;char ch=getchar();bool f=false; while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x=f?-x:x; return ; } template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);} template <typename T> inline void write(T x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10^48); return ; } template <typename T> inline void print(T x){write(x),putchar(' ');} #define ll long long #define ull unsigned long long #define inc(x,y) (x+y>=MOD?x+y-MOD:x+y) #define dec(x,y) (x-y<0?x-y+MOD:x-y) #define min(x,y) (x<y?x:y) #define max(x,y) (x>y?x:y) const int N=1e6+5,M=1e6+5,MOD=1e9+7; char str[N]; int n,m,k,sa[N],rk[N],oldrk[N],id[N],px[N],cnt[N]; bool cmp(int x,int y,int w){return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];} void SA(){ int i,p=0,w; m=300; for(i=1;i<=n;i++) ++cnt[rk[i]=str[i]]; for(i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i; for(w=1;w<n;w<<=1,m=p){ for(p=0,i=n;i>n-w;i--) id[++p]=i; for(i=1;i<=n;i++) if(sa[i]>w) id[++p]=sa[i]-w; memset(cnt,0,sizeof(cnt)); for(i=1;i<=n;i++) ++cnt[px[i]=rk[id[i]]]; for(i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i]; memcpy(oldrk,rk,sizeof(rk)); for(p=0,i=1;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w) ? p : ++p; } return ; } int main(){ read(k);char ch; for(int i=1;i<=k;i++) while(isalpha(ch=getchar())) str[i]=ch; n=k<<1|1; for(int i=1;i<=k;i++) str[i+k+1]=str[k-i+1]; SA(); int l=1,r=k,tot=0; while(l<=r){ if(rk[l]<rk[n+1-r]) putchar(str[l++]); else putchar(str[r--]); if((++tot)%80==0) puts(""); } system("Pause"); return 0; }