BZOJ2119:股市的预测 分治+后缀数组

思路:

一种分治的神做法.建议去看原论文[2011年集训队作业]

#include<map>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define INF 0x3f3f3f3f
 
#define N 50010
 
int n,lim;
 
int len[N];
inline void pre(int n){
    for(int i=2;i<=n;++i)len[i]=len[i>>1]+1;
}
 
int Read[N],s[N],ss[N],v[N],nv[N],q[N],c[N];
inline bool cmp(int x,int y,int hl,int n){
    return v[x]==v[y]&&((x+hl>n&&y+hl>n)||(x+hl<=n&&y+hl<=n&&v[x+hl]==v[y+hl]));
}
struct SuffixArray{
    int rk[N],h[N],sa[N],rmqh[N][16];
    inline void Init(int s[],int n,int m){
        int i,j,k,hl,id;
        for(i=1;i<=m;++i)c[i]=0;
        for(i=1;i<=n;++i)++c[v[i]=s[i]];
        for(i=2;i<=m;++i)c[i]+=c[i-1];
        for(i=n;i>=1;--i)sa[c[v[i]]--]=i;
        for(int d=1;;++d){
            for(hl=1<<(d-1),id=0,i=n-hl+1;i<=n;++i)q[++id]=i;
            for(i=1;i<=n;++i)if(sa[i]>hl)q[++id]=sa[i]-hl;
            for(i=1;i<=m;++i)c[i]=0;
            for(i=1;i<=n;++i)++c[v[q[i]]];
            for(i=2;i<=m;++i)c[i]+=c[i-1];
            for(i=n;i>=1;--i)sa[c[v[q[i]]]--]=q[i];
            for(i=m=1;i<=n;++m,i=j+1){
                for(j=i;j!=n&&cmp(sa[j],sa[j+1],hl,n);++j);
                for(k=i;k<=j;++k)nv[sa[k]]=m;
            }
            if(--m==n)break;
            for(i=1;i<=n;++i)v[i]=nv[i];
        }
         
        for(i=1;i<=n;++i)rk[sa[i]]=i;
        for(i=1;i<=n;++i){
            if(rk[i]==1)continue;
            j=max(0,h[rk[i-1]]-1);
            while(i+j<=n&&sa[rk[i]-1]+j<=n&&s[i+j]==s[sa[rk[i]-1]+j])++j;
            h[rk[i]]=j;
        }
        for(i=1;i<=n;++i)rmqh[i][0]=h[i];
        for(j=1;j<=15;++j)for(i=1;i+(1<<j)-1<=n;++i)rmqh[i][j]=min(rmqh[i][j-1],rmqh[i+(1<<(j-1))][j-1]);
    }
    inline int askrmq(int l,int r){
        return min(rmqh[l][len[r-l+1]],rmqh[r-(1<<len[r-l+1])+1][len[r-l+1]]);
    }
    inline int getlcp(int x,int y){
        int lins=min(rk[x],rk[y]),rins=max(rk[x],rk[y]);
        if(lins==rins)return n-x+1;
        else return askrmq(lins+1,rins);
    }
}Steins,revSteins;
 
inline int lcp(int x,int y){
    return Steins.getlcp(x,y);
}
inline int lcs(int x,int y){
    return revSteins.getlcp(n-x+1,n-y+1);
}
inline bool same(int l1,int l2,int len){
    return lcp(l1,l2)>=len;
}
 
map<int,int>M;int sav[N];
 
long long res;
inline void SteinsGate(int l,int r){
    if(r-l+1<lim+2)return;
    int mid=(l+r)>>1;
    SteinsGate(l,mid),SteinsGate(mid+1,r);
    int prelen,suflen,insl,insr,nowlen;
    for(prelen=0;prelen<lim;++prelen){
        suflen=lim-1-prelen;
        insl=mid-prelen,insr=mid+suflen;
        if(insl>=l&&insr<=r)
            for(nowlen=1;insl-nowlen>=l&&insr+nowlen<=r;++nowlen)if(same(insl-nowlen,insr+1,nowlen))++res;
    }
    int lins,rins,anotherins,Lcp,Lcs,up,down;
    for(anotherins=l;anotherins<=r;++anotherins){
        if((anotherins>mid&&anotherins-mid-1>=lim)||(mid>anotherins&&mid-anotherins-1>=lim)){
            lins=min(mid,anotherins),rins=max(mid,anotherins);
            Lcp=lcp(lins,rins),Lcs=lcs(lins,rins);
            down=-INF,up=INF;
            down=max(down,rins-Lcs-lim+1);//rins-(x+m-1)<=lcs
            up=min(up,lins+Lcp);//x-lins<=lcp
            down=max(down,l-lim+rins-lins);//lins-(rins-(x+m-1))+1>=l
            up=min(up,r+1+lins-rins);//rins+(x-lins)-1<=r
            down=max(down,lins+1),up=min(up,rins-1);//lins<=x<=rins
            down=max(down,lins+2-lim),up=min(up,rins-lim);//lins<=x+m-1<=rins
            if(down<=up){
                res+=up-down+1;
                if(anotherins<mid&&down==anotherins+1)--res;
            }
        }
    }
}
 
int main(){
    #ifndef ONLINE_JUDGE
    freopen("tt.in","r",stdin);
    #endif
    scanf("%d%d",&n,&lim);register int i;
    for(i=0;i<n;++i)scanf("%d",&Read[i]);
    for(--n,i=1;i<=n;++i)s[i]=Read[i]-Read[i-1];
     
    for(i=1;i<=n;++i)sav[i]=s[i];sort(sav+1,sav+n+1);
    int id=0;for(sav[0]=-INF,i=1;i<=n;++i)if(sav[i]!=sav[i-1])M[sav[i]]=++id;
    for(i=1;i<=n;++i)s[i]=M[s[i]];
    for(i=n;i>=1;--i)ss[i]=s[n+1-i];
     
    pre(n),Steins.Init(s,n,id),revSteins.Init(ss,n,id);
     
    SteinsGate(1,n);
     
    cout<<res<<endl;
     
    return 0;
}

POJ3415 Common Substrings 后缀数组+单调栈

思路:

最近好像是被<囚人的旋律>里的主人公的心情所影响,所以效率很低.

例如,这样一道傻逼题我居然傻逼了3个小时!

我整个人都单调栈了.

 

就是求个height,然后在连续块内,我们对于每个属于串A的后缀分别计算出在他前面的属于串B的后缀的影响,在计算出在他之后的属于B的后缀的影响.

然后我们从前到后,再从后到前扫描一边,利用一个单调栈维护一下属于串B的后缀即可.

说起来简单...不过写起来...一定是我太鶸了.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;

#define N 200010
char s1[N],s2[N],s[N];int v[N],tv[N],q[N],c[N],sa[N];
inline bool cmp(int x,int y,int hl,int n){
	return v[x]==v[y]&&((x+hl>=n&&y+hl>=n)||(x+hl<n&&y+hl<n&&v[x+hl]==v[y+hl]));
}
inline void calcsa(char*s,int n,int m,int*sa){
	register int i,j,k;int id,hl;
	for(i=0;i<m;++i)c[i]=0;
	for(i=0;i<n;++i)++c[v[i]=s[i]];
	for(i=1;i<m;++i)c[i]+=c[i-1];
	for(i=n-1;i>=0;--i)sa[--c[v[i]]]=i;
	for(int d=1;;++d){
		for(hl=1<<(d-1),id=0,i=n-hl;i<n;++i)q[id++]=i;
		for(i=0;i<n;++i)if(sa[i]>=hl)q[id++]=sa[i]-hl;
		for(i=0;i<m;++i)c[i]=0;
		for(i=0;i<n;++i)++c[v[q[i]]];
		for(i=1;i<m;++i)c[i]+=c[i-1];
		for(i=n-1;i>=0;--i)sa[--c[v[q[i]]]]=q[i];
		for(i=m=0;i<n;i=j+1,++m){
			for(j=i;j<n-1&&cmp(sa[j],sa[j+1],hl,n);++j);
			for(k=i;k<=j;++k)tv[sa[k]]=m;
		}for(i=0;i<n;++i)v[i]=tv[i];
		if(m==n)break;
	}
}
int rk[N],h[N];
inline void calch(char*s,int n,int*sa,int*rk,int*h){
	register int i,j;
	for(i=0;i<n;++i)rk[sa[i]]=i;
	for(i=0;i<n;++i)if(rk[i]){
		j=max(0,h[rk[i-1]]-1);while(i+j<n&&sa[rk[i]-1]+j<n&&s[i+j]==s[sa[rk[i]-1]+j])++j;h[rk[i]]=j;
	}
}

int lim;

struct Case{
	int v,c;
	Case(){}
	Case(int _v,int _c):v(_v),c(_c){}
}stk[N];
int top,nowcnt;long long sum[N];int cnt[N];

int belong[N];
int main(){
	int i,j,k;
	while(scanf("%d",&lim)&&lim){
		scanf("%s%s",s1,s2);int len1=strlen(s1),len2=strlen(s2);
		memset(s,0,sizeof s);int len=0;for(i=0;i<len1;++i)belong[len]=1,s[len++]=s1[i];s[len++]='$';for(i=0;i<len2;++i)belong[len]=2,s[len++]=s2[i];
		calcsa(s,len,256,sa),calch(s,len,sa,rk,h);

		LL ans=0;int tmp;
		for(i=1;i<len;){
			if(h[i]>=lim){
				for(j=i;j<len&&h[j]>=lim;++j);
				for(top=0,h[i-1]=h[i],tmp=h[j],h[j]=h[j-1],k=i-1;k<j;++k){
					nowcnt=0;
					while(top&&stk[top].v>=h[k])nowcnt+=stk[top--].c;
					if(nowcnt)stk[++top]=Case(h[k],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					if(belong[sa[k]]==2)ans+=sum[top]-(LL)cnt[top]*(lim-1);
					else{
						nowcnt=1;while(top&&stk[top].v>=h[k+1])nowcnt+=stk[top--].c;
						stk[++top]=Case(h[k+1],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					}
					
				}
				for(top=0,k=j-1;k>=i-1;--k){
					nowcnt=0;
					while(top&&stk[top].v>=h[k+1])nowcnt+=stk[top--].c;
					if(nowcnt)stk[++top]=Case(h[k+1],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					if(belong[sa[k]]==2)ans+=sum[top]-(LL)cnt[top]*(lim-1);
					else{
						nowcnt=1;while(top&&stk[top].v>=h[k])nowcnt+=stk[top--].c;
						stk[++top]=Case(h[k],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					}
				}h[j]=tmp;
				i=j;
			}else++i;
		}
		cout<<ans<<endl;
	}
	return 0;
}

POJ3693 Maximum repetition substring 后缀数组

思路:

这个题简直丧心病狂.终于是过了.

注意到重复出现一次的字串必定是字典序最小的字符.我们只考虑重复出现不少于两次的子串.

首先我们枚举重复出现的每一小段的长度\(l\).考虑若干个端点\(0,l,2l,...,i*l,(i+1)*l\),若存在每一小段长度为\(l\)且连续重复出现至少两次的子串,则至少覆盖两个端点.

因此我们枚举相邻的两个端点,分别找出向前最多能匹配多少,向后最多能匹配多少,我们就找到了两个完全相等的段,而且其错开了\(l\)的长度.不妨令其长度为\(d\),则(通过画图)此时的最大重复次数为\(\frac{d}{l}+1\).

如果不要求字典序的话,我们直接这样找到答案就行了.用后缀数组预处理一大堆东西能做到各种询问\(O(1)\)时间复杂度为:

\[\frac{n}{1}+\frac{n}{2}+...+\frac{n}{n}=O(nlogn)\]

因此总的时间复杂度为\(O(nlogn)\).

关键是要求字典序最小.

我们发现每次找到两个完全相等的段时都存在很多合法的答案,也就是若干长度相等,但起点位置不同的子串.我们预处理出起点区间内字典序最小的子串,并利用这个更新答案即可.

不管怎么样,反正时间复杂度\(O(nlogn)\).

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 100010
int len;
char s[N],ss[N];

int v[N],tmpv[N],cnt[N],q[N],sa1[N],sa2[N];
inline bool cmp(int x,int y,int hl,int n){
	return v[x]==v[y]&&((x+hl>=n&&y+hl>=n)||(x+hl<n&&y+hl<n&&v[x+hl]==v[y+hl]));
}
inline void calcsa(char*s,int n,int m,int*sa){
	register int i,j,k;int hl,id;
	for(i=0;i<m;++i)cnt[i]=0;
	for(i=0;i<n;++i)++cnt[v[i]=s[i]];
	for(i=1;i<m;++i)cnt[i]+=cnt[i-1];
	for(i=n-1;i>=0;--i)sa[--cnt[v[i]]]=i;
	for(int d=1;;++d){
		for(hl=1<<(d-1),id=0,i=n-hl;i<n;++i)q[id++]=i;
		for(i=0;i<n;++i)if(sa[i]>=hl)q[id++]=sa[i]-hl;
		for(i=0;i<m;++i)cnt[i]=0;
		for(i=0;i<n;++i)++cnt[v[q[i]]];
		for(i=1;i<m;++i)cnt[i]+=cnt[i-1];
		for(i=n-1;i>=0;--i)sa[--cnt[v[q[i]]]]=q[i];
		for(m=i=0;i<n;m++,i=j+1){
			for(j=i;j<n-1&&cmp(sa[j],sa[j+1],hl,n);++j);
			for(k=i;k<=j;++k)tmpv[sa[k]]=m;
		}for(i=0;i<n;++i)v[i]=tmpv[i];
		if(m==n)break;
	}
}
int rk1[N],rk2[N],h1[N],h2[N];
inline void calch(char*s,int n,int*sa,int*rk,int*h){
	register int i,j;
	for(i=0;i<n;++i)rk[sa[i]]=i;
	for(i=0;i<n;++i)if(rk[i]){
		j=max(0,h[rk[i-1]]-1);while(i+j<n&&sa[rk[i]-1]+j<n&&s[i+j]==s[sa[rk[i]-1]+j])++j;h[rk[i]]=j;
	}
}
int pre[N],rh1[N][21],rh2[N][21];
inline void Initlen(int n){
	for(register int i=2;i<=n;++i)pre[i]=pre[i>>1]+1;
}
inline void Initrmq(int*source,int rm[][21],int n){
	register int i,j;
	for(i=0;i<n;++i)rm[i][0]=source[i];
	for(j=1;j<=20;++j)for(i=0;i+(1<<j)-1<n;++i)rm[i][j]=min(rm[i][j-1],rm[i+(1<<(j-1))][j-1]);
}
inline int askmin(int rm[][21],int l,int r){
	return min(rm[l][pre[r-l+1]],rm[r-(1<<pre[r-l+1])+1][pre[r-l+1]]);
}
inline int getlcp(int x,int y){
	static int rx,ry;rx=rk1[x],ry=rk1[y];if(rx>ry)swap(rx,ry);
	return x==y?len-x:askmin(rh1,rx+1,ry);
}
inline int getlcs(int x,int y){
	static int rx,ry;rx=rk2[len-1-x],ry=rk2[len-1-y];if(rx>ry)swap(rx,ry);
	return x==y?x+1:askmin(rh2,rx+1,ry);
}
int mins[N][21];
inline void Initmins(){
	register int i,j;
	for(i=0;i<len;++i)mins[i][0]=i;
	for(j=1;j<=20;++j)for(i=0;i+(1<<j)-1<len;++i)mins[i][j]=rk1[mins[i][j-1]]<rk1[mins[i+(1<<(j-1))][j-1]]?mins[i][j-1]:mins[i+(1<<(j-1))][j-1];
}
inline int getmins(int l,int r){
	int sl=mins[l][pre[r-l+1]],sr=mins[r-(1<<pre[r-l+1])+1][pre[r-l+1]];
	return rk1[sl]<rk1[sr]?sl:sr;
}

struct Ans{
	int ins,len,t;
	Ans(){}
	Ans(int _ins,int _len,int _t):ins(_ins),len(_len),t(_t){}
	bool operator==(const Ans&B)const{return ins==B.ins&&len==B.len&&t==B.t;}
	bool operator<(const Ans&B)const{
		if(t!=B.t)return t>B.t;
		if(ins==B.ins)return len<B.len;
		int rx=rk1[ins],ry=rk1[B.ins],lcp=getlcp(ins,B.ins);
		if(len>lcp&&B.len>lcp)return rx<ry;else return len<B.len;
	}
}res;
inline void upd(Ans&x,Ans y){if(y<x)x=y;}

int main(){
	//freopen("tt.in","r",stdin);
	int test=0;
	register int i,j;
	while(scanf("%s",s)&&s[0]!='#'){
		printf("Case %d: ",++test); 
		len=strlen(s);for(i=0;i<len;++i)ss[i]=s[len-1-i];
		calcsa(s,len,256,sa1),calcsa(ss,len,256,sa2);
		calch(s,len,sa1,rk1,h1),calch(ss,len,sa2,rk2,h2);
		Initlen(len);
		Initrmq(h1,rh1,len),Initrmq(h2,rh2,len);
		Initmins();
		res=Ans(0,0,0);
		int lcp,lcs,d;
		for(int l=1;l<=len/2;++l){
			for(i=0;(i+1)*l<len;++i){
				lcp=getlcp(i*l,(i+1)*l);
				lcs=getlcs(i*l,(i+1)*l);
				d=lcp+lcs-1;
				upd(res,Ans(getmins(i*l-lcs+1,(i+1)*l+lcp-(d/l+1)*l),(d/l+1)*l,d/l+1));
			}
		}
		if(res.t==1){
			int c=~0U>>1;for(i=0;i<len;++i)c=min(c,(int)s[i]);printf("%c\n",c);
		}
		else{for(j=0;j<res.len;++j)putchar(s[res.ins+j]);puts("");}	
	}
	return 0;
}

BZOJ3796: Mushroom追妹纸 后缀数组+二分+暴力

BZOJ3238: [Ahoi2013]差异 后缀数组+单调队列