zoukankan      html  css  js  c++  java
  • 【bzoj2806】 Ctsc2012—Cheat

    http://www.lydsy.com/JudgeOnline/problem.php?id=2806 (题目链接)

    题意

      给出M个字符串组成“标准库”。定义L表示将一个字符串分成若干段,每一段的长度不小于L,其中是在标准库中任一字符串的子串的字符“段”的长度之和不小于原字符串长度之和的90%。N个询问,每个给出一个字符串,求其满足条件的最大的L。

    Solution

      对于每一个询问,我们在线做,话说离线怎么做,整体二分吗→_→

      很显然,这应该是要二分答案,考虑怎么check。我们想到了dp:${f_i}$表示后缀${i}$的最长覆盖长度。

    $${f_{i}=Max{f_{i+1},f_j+j-i},i+L_0<=j<=R_i+i}$$

      其中${L_0}$表示当前二分的答案,${R_i}$表示从第${i}$位开始能够匹配到的最长的连续段长度。注意这个要倒着做→_→

      然而这个dp是${O(n^2)}$的,我们还需要优化,加上一个括号:

    $${f_i=Max{f_{i+1},(f_j+j)-i},i+L_0<=j<=R_i+i}$$

      于是${f_j+j}$就只与${j}$有关了,我们考虑单调队列。如果队首因为它的位置${>=R_i+i}$而被踢出了队列,那么它必然不会因为后面串的${R_i}$长度增大而被加回来,因为每往后面挪一格长度只可能+1,然后又因为i会-1,所以就是不变的。

      那么我们就可以${O(nlogn)}$的求解每一个询问了。

      那么只剩下一个问题,${R_i}$怎么求。考虑后缀数组。将所有串接在一起,求一个后缀数组,然后求出height,那么如果一个后缀是询问串的后缀,与其最近的“标准库”中的后缀的height就是它的${R_i}$。然后倍增构后缀数组就TLE飞了→_→,等下补一发后缀自动机的。

      UPD 2017.1.19:

      这几天一直在考试,拖到了现在。原来就是把“标准库”中的串用分隔符接起来构后缀自动机,之后每一个询问串在上面跑匹配就可以轻松的求出${R_i}$了,我还写了发后缀自动机构后缀数组,然而分隔符太大了,开不下→_→。

      因为之前的dp是倒着求的,所以这里的后缀自动机和匹配都是倒着来的→_→,懒得再去改dp和二分答案了。。

    细节

      构造后缀数组时桶的大小要注意;求${R_i}$的时候要想清楚→_→。

      后缀自动机匹配的时候长度要随着匹配失败跳parent树的移动而改变。

    代码(后缀数组)

    // bzoj2806
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #define LL long long
    #define inf 1<<30
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=2000010;
    int f[maxn],S[maxn],a[maxn],vis[maxn],q[maxn],pl[maxn],pr[maxn],R[maxn];
    int rank[maxn],sa[maxn],height[maxn];
    char s[maxn];
    int n,m,ans;
    
    namespace Suffix {
    	int wa[maxn],wb[maxn],ww[maxn];
    	bool cmp(int *r,int a,int b,int l) {
    		return r[a]==r[b] && r[a+l]==r[b+l];
    	}
    	void da(int *r,int *sa,int n,int m) {
    		int i,j,p,*x=wa,*y=wb;
    		for (i=0;i<=m;i++) ww[i]=0;
    		for (i=1;i<=n;i++) ww[x[i]=r[i]]++;
    		for (i=1;i<=m;i++) ww[i]+=ww[i-1];
    		for (i=n;i>=1;i--) sa[ww[x[i]]--]=i;
    		for (p=0,j=1;p<n;j*=2,m=p) {
    			for (p=0,i=n-j+1;i<=n;i++) y[++p]=i;
    			for (i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
    			for (i=0;i<=m;i++) ww[i]=0;
    			for (i=1;i<=n;i++) ww[x[y[i]]]++;
    			for (i=1;i<=m;i++) ww[i]+=ww[i-1];
    			for (i=n;i>=1;i--) sa[ww[x[y[i]]]--]=y[i];
    			for (swap(x,y),p=x[sa[1]]=1,i=2;i<=n;i++)
    				x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p : ++p;
    		}
    	}
    	void calheight(int *r,int *sa,int n) {
    		for (int i=1;i<=n;i++) rank[sa[i]]=i;
    		for (int k=0,i=1;i<=n;i++) {
    			if (k) k--;
    			int j=sa[rank[i]-1];
    			while (r[i+k]==r[j+k]) k++;
    			height[rank[i]]=k;
    		}
    	}
    }
    using namespace Suffix;
    
    bool dp(int x,int L0) {
    	for (int i=pl[x];i<=pr[x];i++) f[i]=0;
    	int l=1,r=1;q[1]=pr[x]+1;
    	for (int i=pr[x]-L0+1;i>=pl[x];i--) {
    		while (l<=r && q[l]>i+R[i]) l++;
    		f[i]=f[i+1];
    		if (l<=r) f[i]=max(f[i],f[q[l]]+q[l]-i);
    		if (i+L0-1<=pr[x]) {
    			while (l<=r && f[q[r]]+q[r]<f[i+L0-1]+i+L0-1) r--;
    			q[++r]=i+L0-1;
    		}
    	}
    	return 10*f[pl[x]]>=(pr[x]-pl[x]+1)*9;
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	int len=0;S[0]=inf;
    	for (int i=1;i<=m;i++) {
    		scanf("%s",s+1);
    		for (int j=1;j<=strlen(s+1);j++) S[++len]=s[j]-'0';
    		S[++len]=i+2;
    	}
    	for (int i=1;i<=n;i++) {
    		scanf("%s",s+1);
    		int tmp=strlen(s+1);
    		pl[i]=len+1;
    		for (int j=1;j<=tmp;j++) S[++len]=s[j]-'0',vis[len]=1;
    		pr[i]=len;
    		S[++len]=i+m+2;
    	}
    	da(S,sa,len,1000000);
    	calheight(S,sa,len);
    	for (int l=inf,i=2;i<=len;i++) {
    		if (i==2) while (vis[sa[i-1]] && vis[sa[i]] && i<=len) i++;
    		if (!vis[sa[i]]) l=inf;
    		else {
    			l=min(l,height[i]);
    			R[sa[i]]=max(R[sa[i]],l);
    		}
    	}
    	for (int l=inf,i=len-1;i>=1;i--) {
    		if (i==len-1) while (vis[sa[i+1]] && vis[sa[i]] && i>=1) i--;
    		if (!vis[sa[i]]) l=inf;
    		else {
    			l=min(l,height[i+1]);
    			R[sa[i]]=max(R[sa[i]],l);
    		}
    	}
    	for (int i=1;i<=n;i++) {
    		int l=0,r=pr[i]-pl[i]+1,ans=0;
    		while (l<=r) {
    			int mid=(l+r)>>1;
    			if (dp(i,mid)) ans=mid,l=mid+1;
    			else r=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

     代码(后缀自动机)

    // bzoj2806
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #define LL long long
    #define inf 1<<30
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=2000010;
    int f[maxn],q[maxn],R[maxn];
    char s[maxn];
    int n,m,N,ans;
    
    namespace SAM {
    	int par[maxn<<1],len[maxn<<1],pos[maxn<<1],ch[maxn>>1][10];
    	int last,Dargen,sz;
    
    	void Extend(int c) {
    		int np=++sz,p=last;last=np;
    		len[np]=len[p]+1;
    		for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;
    		if (!p) par[np]=Dargen;
    		else {
    			int q=ch[p][c];
    			if (len[p]+1==len[q]) par[np]=q;
    			else {
    				int nq=++sz;len[nq]=len[p]+1;
    				memcpy(ch[nq],ch[q],sizeof(ch[q]));
    				par[nq]=par[q];
    				par[np]=par[q]=nq;
    				for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
    			}
    		}
    	}
    	void match() {
    		int ll=0;
    		for (int p=Dargen,i=n;i>=1;i--) {
    			while (p>1 && !ch[p][s[i]-'0']) p=par[p],ll=len[p];
    			if (ch[p][s[i]-'0']) p=ch[p][s[i]-'0'],ll++;
    			else ll=0;
    			R[i]=ll;
    		}
    	}
    }
    using namespace SAM;
    
    bool dp(int x,int L0) {
    	for (int i=1;i<=n+1;i++) f[i]=0;
    	int l=1,r=1;q[1]=n+1;
    	for (int i=n-L0+1;i>=1;i--) {
    		while (l<=r && q[l]>i+R[i]) l++;
    		f[i]=f[i+1];
    		if (l<=r) f[i]=max(f[i],f[q[l]]+q[l]-i);
    		if (i+L0-1<=n) {
    			while (l<=r && f[q[r]]+q[r]<f[i+L0-1]+i+L0-1) r--;
    			q[++r]=i+L0-1;
    		}
    	}
    	return 10*f[1]>=n*9;
    }
    int main() {
    	scanf("%d%d",&N,&m);
    	SAM::sz=SAM::Dargen=SAM::last=1;
    	for (int i=1;i<=m;i++) {
    		scanf("%s",s+1);
    		for (int i=strlen(s+1);i>=1;i--) Extend(s[i]-'0');
    		Extend(2);
    	}
    	for (int i=1;i<=N;i++) {
    		scanf("%s",s+1);
    		n=strlen(s+1);
    		match();
    		int l=0,r=n,ans=0;
    		while (l<=r) {
    			int mid=(l+r)>>1;
    			if (dp(i,mid)) ans=mid,l=mid+1;
    			else r=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    VisualSVN-Server windows 版安装时报错 "Service 'VisualSVN Server' failed to start. Please check VisualSVN Server log in Event Viewer for more details."
    Pytest 单元测试框架之初始化和清除环境
    Pytest 单元测试框架入门
    Python(email 邮件收发)
    Python(minidom 模块)
    Python(csv 模块)
    禅道简介
    2020年最好的WooCommerce主题
    Shopify网上开店教程(2020版)
    WooCommerce VS Magento 2020:哪个跨境电商自建站软件更好?
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6294884.html
Copyright © 2011-2022 走看看