zoukankan      html  css  js  c++  java
  • 题解-CTSC2012 熟悉的文章

    Problem

    bzoj

    题目大意:给定多个标准串和一个文本串,全部为01串,如果一个串长度不少于(L)且是任意一个标准串的子串,那么它是“熟悉”的。对于文本串(A),把(A)分割成若干段子串,其中“熟悉”的子串的长度总和不少于(A)总长度的(90\%),那么该(L)是可行的。求可行的(L)最大值

    Solution

    前置技能:二分答案、SAM、Dp、单调队列

    字符串长在L上下对答案贡献是断崖式的,按套路二分L

    再根据对序列分段问题的直觉可以得到dp方程:(f[i]=max(f[i-1],f[j]+i-j),jleq i-L且s[j…i]是熟悉的)

    这样复杂度加上各种优化是(O(n^2))(O(n^3))不等的

    考虑到对于(i),合法的(j)一定是连续的,可以用后缀自动机预处理出每一个字符(i)向左最长的熟悉的串位置(orz[i])

    dp方程为:(f[i]=max(f[i-1],f[j]+i-j),jin[i-orz[i],i-L])

    发现dp方程可以用单调队列优化:队列里存(f[i]-i)即可

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define rg register
    
    const int N=2001000;
    int pre[N],stp[N],ch[N][2];
    int f[N],q[N],orz[N];
    int n,m,len,tot=1,lst=1,L,R;
    char s[N];
    
    inline void ins(int x){
    	int p=lst,np=++tot;
    	lst=np;stp[np]=stp[p]+1;
    	while(p&&!ch[p][x])ch[p][x]=np,p=pre[p];
    	if(!p)pre[np]=1;
    	else {
    		int q=ch[p][x];
    		if(stp[q]==stp[p]+1)pre[np]=q;
    		else {
    			int nq=++tot;stp[nq]=stp[p]+1;
    			//*ch[nq]=*ch[q];
    			ch[nq][0]=ch[q][0],ch[nq][1]=ch[q][1];
    			pre[nq]=pre[q];
    			pre[q]=pre[np]=nq;
    			while(ch[p][x]==q)ch[p][x]=nq,p=pre[p];
    		}
    	}return ;
    }
    
    inline int check(int li){
    	int he(1),ta(0);
    	for(rg int i=1;i<=len;++i){
    		f[i]=f[i-1];
    		if(i<li)continue;
    		while(he<=ta&&f[q[ta]]-q[ta]<=f[i-li]-i+li)--ta;
    		q[++ta]=i-li;
    		while(he<=ta&&q[he]<i-orz[i])++he;
    		if(he<=ta)f[i]=max(f[i],f[q[he]]+i-q[he]);
    	}return f[len]*10>=len*9;
    }
    
    void PRE(){
    	scanf("%s",s+1);
    	len=strlen(s+1);
    	L=0;R=len;
    	int nw(1),cnt(0);
    	for(rg int i=1;i<=len;++i){
    		int x=s[i]-'0';
    		if(ch[nw][x])++cnt,nw=ch[nw][x];
    		else {
    			while(nw&&!ch[nw][x])nw=pre[nw];
    			if(nw)cnt=stp[nw]+1,nw=ch[nw][x];
    			else nw=1,cnt=0;
    		}
    		orz[i]=cnt;
    	}return ;
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	while(m--){
    		scanf("%s",s);lst=1;
    		for(rg int i=0;s[i];++i)ins(s[i]-'0');
    	}
    	while(n--){
    		PRE();
    		while(L<R){
    			int mid(L+R+1>>1);
    			if(check(mid))L=mid;
    			else R=mid-1;
    		}
    		printf("%d
    ",L);
    	}return 0;
    }
    
  • 相关阅读:
    使用委派代替继承
    《重构,改善既有代码的设计》读书笔记
    理解C指针: 一个内存地址对应着一个值
    C#实现窗口最小化到系统托盘
    C#中访问私有成员--反射
    不要在构造函数中调用可重写的方法
    链表解决约瑟夫环问题
    C数据结构(文件操作,随机数,排序,栈和队列,图和遍历,最小生成树,最短路径)程序例子
    java this,super简单理解
    数据与计算机通信习题
  • 原文地址:https://www.cnblogs.com/penth/p/9233454.html
Copyright © 2011-2022 走看看