洛谷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;
}
posted @ 2020-12-29 15:45  __Anchor  阅读(75)  评论(0编辑  收藏  举报