• UVALive


    题面

    今天复习了一下后缀数组。。。感觉忘得一干二净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
    ",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
    ",M,P);
    }
    
    int main(){
    	while(scanf("%d",&m)==1&&m){
    		init(),scanf("%s",s),n=strlen(s);
    		if(m==1){ printf("%d %d
    ",n,0); continue;}
    		
    		build();
    		
    		solve();
    	}
    	
    	return 0;
    }
    

      

  • 相关阅读:
    矩阵乘法(二):利用矩阵快速幂运算完成递推
    更改codeblock编译后程序的图标
    如何在VS2008下使用FLTK
    Python type() 函数
    Python range() 函数用法
    Python len()方法
    Python filter() 函数
    Python bool() 函数
    数据类型
    JAVA标识符
  • 原文地址:https://www.cnblogs.com/JYYHH/p/11279215.html
Copyright © 2020-2023  润新知