zoukankan      html  css  js  c++  java
  • CTSC2012-Cheat

    题意

    给出一些母01串,多次询问,每次询问一个01串,问一个最大的(L),使得可以在询问串中选出若干个不相交的,长度大于等于(L)的子串,这些子串都在母串中出现过,且子串的长度和大于等于询问串总长的(90\%)

    文件大小小于等于1100000字节。

    分析

    首先如果一个(L)可行,那么小于(L)的也是可行的,因为是“长度大于等于”。于是我们就二分这个(L),转化成判定问题。

    把分割序列这类问题可以考虑dp。设(f_i)为前(i)位能分割出来符合要求的最大子串长度和。显然有:

    [f_i=max egin{cases} f_{i-1} \ f_j+(i-j) && i-jin [g_i,L] end{cases} ]

    第一种情况表示从前一个直接转移过来,即不以(i)结尾的。第二种表示以(i)结尾的,其中(g_i)表示第(i)位前面最多可以在母串中匹配多长。这可以通过广义后缀自动机方便地算出来(跳link重置为len,匹配加一)。

    显然如果直接暴力dp的话是(O(n^2))的,必须考虑优化。只考虑第二种情况:

    [egin{aligned} f_i=f_j+i-j && i-jin [g_i,L] \ f_i=i+(f_j-j) && jin[i-g_i,i-L] end{aligned} ]

    可以注意到,(i-L)每次往后移动一格,而(i-g_i)的值是单调不减的,因为每次(i)加一,(g_i)最多加一,即最多多匹配一位,不可能突然多出来匹配的几位,否则就会与前面的(g)值矛盾。这就是说,(j)的可行区间是单调不减的,所以可以用单调队列优化到(O(n))。队列为队头小,队尾大,每次在队头插入(i-L)处的值,如果队头比它小就弹出。在队尾把出了合法区间中的值弹出,取队尾即可。

    单次询问的复杂度为(O(lenlog len))

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=1.1e6+10;
    const int maxc=2;
    char s[maxn];
    int f[maxn],g[maxn],n,que[maxn],ql,qr;
    struct SAM {
    	int t[maxn<<1][maxc],len[maxn<<1],link[maxn<<1],last,tot;
    	SAM ():tot(1) {}
    	void reset() {last=1;}
    	void add(int x) {
    		if (t[last][x]) {
    			int p=t[last][x];
    			if (len[p]==len[last]+1) {
    				last=p;
    				return;
    			} else {
    				int q=++tot;
    				len[q]=len[last]+1;
    				memcpy(t[q],t[p],sizeof t[p]);
    				for (int j=last;j && t[j][x]==p;j=link[j]) t[j][x]=q;
    				link[q]=link[p],link[p]=q;
    				last=q;
    				return;
    			}
    		}
    		int nw=++tot,i;
    		len[nw]=len[last]+1;
    		for (i=last;i && !t[i][x];i=link[i]) t[i][x]=nw;
    		if (i) {
    			int p=t[i][x];
    			if (len[p]==len[i]+1) link[nw]=p; else {
    				int q=++tot;
    				len[q]=len[i]+1;
    				memcpy(t[q],t[p],sizeof t[p]);
    				for (int j=i;j && t[j][x]==p;j=link[j]) t[j][x]=q;
    				link[q]=link[p],link[p]=link[nw]=q;
    			}
    		} else link[nw]=1;
    		last=nw;
    	}
    	void prepare() {
    		int now=1,mat=0;
    		for (int i=1;i<=n;++i) {
    			int x=s[i]-'0';
    			while (now!=1 && !t[now][x]) now=link[now],mat=len[now];
    			if (t[now][x]) now=t[now][x],++mat;
    			g[i]=mat;
    		}
    	}
    } sam;
    bool dp(int L) {
    	ql=1,qr=0;
    	for (int i=L;i<=n;++i) {
    		f[i]=f[i-1];
    		while (ql<=qr && f[que[qr]]-que[qr]<f[i-L]-i+L) --qr;
    		que[++qr]=i-L;
    		while (ql<=qr && que[ql]<i-g[i]) ++ql;
    		if (ql<=qr) f[i]=max(f[i],f[que[ql]]+i-que[ql]);
    	}
    	return 10*f[n]>=9*n;
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    #endif
    	int q,m;
    	scanf("%d%d",&q,&m);
    	for (int i=1;i<=m;++i) {
    		scanf("%s",s+1);
    		int len=strlen(s+1);
    		sam.reset();
    		for (int i=1;i<=len;++i) sam.add(s[i]-'0');
    	}
    	while (q--) {
    		scanf("%s",s+1);
    		n=strlen(s+1);
    		memset(g,0,(sizeof g[0])*(n+1));
    		sam.prepare();
    		int l=1,r=n,ans;
    		while (l<=r) {
    			int mid=(l+r)>>1;
    			memset(f,0,(sizeof f[0])*(n+1));
    			if (dp(mid)) ans=mid,l=mid+1; else r=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    尝试在mac上用dotnet cli运行asp.net core示例程序
    帅呆了!ASP.NET Core每秒能处理115万个请求
    EF(Entity Framework)多对多关系下用LINQ实现"NOT IN"查询
    前端工程师和设计师必读文章推荐【系列三十四】
    Plyr – 简单,灵活的 HTML5 媒体播放器
    20种新颖的按钮风格和效果【附源码下载】
    12款最佳的 WordPress 语法高亮插件推荐
    MaterialUp
    React Native – 使用 JavaScript 开发原生应用
    推荐15款创建漂亮幻灯片的 jQuery 插件
  • 原文地址:https://www.cnblogs.com/owenyu/p/7148813.html
Copyright © 2011-2022 走看看