1. 程式人生 > 實用技巧 >洛谷P6140&P2870 [USACO07NOV]Best Cow Line S

洛谷P6140&P2870 [USACO07NOV]Best Cow Line S

題意

傳送門

給定一個字串,每次珂以取出當前字串的頭或者尾,將其加入答案串中(放到最後面),要求必須把該字串取完,求字典序最小的方案

分析

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;
}