UVALive - 4513 Stammering Aliens

题面

 

今天复习了一下后缀数组。。。感觉忘得一干二净hhhhh

至于后缀数组是什么怎么写之类的这里就不介绍了,说一下怎么用它做这个题。

我们做完一遍后缀数组,可以得到 rank[i] (表示下标为i开始的后缀的字典序排名),sa[i](可以理解成rank[i]的反函数?为字典序排名为i的后缀的下标),h[i](字典序排名为i的后缀与字典序排名为i-1的后缀的lcp)这三个数组,接下来我们只需要O(N)扫一遍,对于每个 i>=m 求一下 min(h[i],h[i-1],.....,h[i-m+2]) 并用其来更新答案。 当然,位置的更新则是 max(sa[i],sa[i-1],.....,sa[i-m+1])。

 

先不说怎么O(N)实现这个,为什么排好序后直接用连续的一段更新答案就可以了?这是因为我们如果选的不是连续的一段,那么把最左端和最右端之间没选的元素填上,lcp并不会变化, 而出现次数反而会增多,所以可以证明连续的一段一定可以找到答案。

 

然后我们发现 ,要求的 min(h[i],h[i-1],.....,h[i-m+2])和 max(sa[i],sa[i-1],.....,sa[i-m+1]),区间长度是固定的,每次右端点右移一位,所以自然可以想到单调队列优化。

 

(顺带留个板子,以后就懒得手打后缀数组了累煞我也)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=40005;

int r[N*2],rx[N],sa[N],sax[N],M,P,cc[N];
int h[N],sec[N],n,m,q[N],L,R,Q[N],l,rr;
char s[N];

inline void init(){
	memset(s,0,sizeof(s));
	memset(cc,0,sizeof(cc));
	memset(sa,0,sizeof(sa));
	memset(r,0,sizeof(r));	
}

inline void build(){
	for(int i=0;i<n;i++) cc[s[i]]++;
	for(int i=1;i<=500;i++) cc[i]+=cc[i-1];
	for(int i=0;i<n;i++) sa[cc[s[i]]--]=i;
	for(int i=1;i<=n;i++){
		r[sa[i]]=i;
		if(i>1&&s[sa[i]]==s[sa[i-1]]) r[sa[i]]=r[sa[i-1]];
	}
	
	for(int k=1;k<n;k<<=1){
		fill(cc,cc+n+1,0);
		
		for(int i=0;i<n;i++) cc[sec[i]=r[i+k]]++;
		for(int i=n-1;i>=0;i--) cc[i]+=cc[i+1];
		for(int i=0;i<n;i++) sax[cc[sec[i]]--]=i;
		
		fill(cc,cc+n+1,0);
		
		for(int i=0;i<n;i++) cc[r[i]]++;
		for(int i=1;i<=n;i++) cc[i]+=cc[i-1];
		for(int i=1;i<=n;i++) sa[cc[r[sax[i]]]--]=sax[i];
		for(int i=1;i<=n;i++){
            rx[sa[i]]=i;
            if(i>1&&r[sa[i]]==r[sa[i-1]]&&sec[sa[i]]==sec[sa[i-1]]) rx[sa[i]]=rx[sa[i-1]];
		}
		
		for(int i=0;i<n;i++) r[i]=rx[i];
	}
	
	int now=0,j,mx;
	for(int i=0;i<n;i++){
		if(r[i]==1){
			h[r[i]]=now=0;
			continue;
		}
		
		if(now) now--;
		j=sa[r[i]-1],mx=max(i,j);
		while(mx+now<n&&s[i+now]==s[j+now]) now++;
		
		h[r[i]]=now;
	}
}

inline void solve(){
	m--,L=1,R=0,Q[l=rr=1]=1,M=P=0;
	
//	for(int i=1;i<=n;i++) printf("%d %d\n",sa[i],h[i]);
	
	for(int i=2;i<=n;i++){
		while(L<=R&&h[i]<=h[q[R]]) R--;
		q[++R]=i;
		while(L<=R&&i-q[L]>=m) L++;
		
		while(l<=rr&&sa[i]>=sa[Q[rr]]) rr--;
		Q[++rr]=i;
		while(l<=rr&&i-Q[l]>m) l++;		
		
		if(i<=m) continue;
		
		if(h[q[L]]>M) M=h[q[L]],P=sa[Q[l]];
		else if(h[q[L]]==M) P=max(P,sa[Q[l]]);
	}
	
	if(!M) puts("none");
	else printf("%d %d\n",M,P);
}

int main(){
	while(scanf("%d",&m)==1&&m){
		init(),scanf("%s",s),n=strlen(s);
		if(m==1){ printf("%d %d\n",n,0); continue;}
		
		build();
		
		solve();
	}
	
	return 0;
}

  

posted @ 2019-07-31 21:31  蒟蒻JHY  阅读(142)  评论(0编辑  收藏  举报