• BZOJ 2806 cheat


    首先这个题目显然是要二分转换成判断可行性的

    之后我们考虑DP

    设f(i)表示 1->i 熟悉的子串的长度的最大值

    那么对于i这个点,要么不在熟悉的子串中,要么在熟悉的子串中

    所以得到

    f(i)=max(f(i-1),f(j)+i-j);

    其中i-j是划分的熟悉的子串的长度,要满足以下条件:

    1、i-j>=k (k为二分出来的值)

    2、[j+1,i]这段串是给定标准文章库的一个子串

    我们又知道若[j+1,i]是一个满足条件的子串,那么[j+2,i]也一定满足条件

    假设我们已知最小的p满足[p+1,i]是一个满足条件的子串,定义L=i-p

    那么条件2转化为 i-j<=L

    求特定区间的最大值,且左右端点是单调的,我们是可以用单调队列的

    那么现在问题就是求解最小的p使得[p+1,i]满足条件

    即求解给定串S的一个前缀的最长满足条件的后缀,这时可以用后缀自动机做的

    每次只需要在上次的基础上顺着parent树向上寻找匹配就可以了

    注意:当寻找到SAM上的一个可匹配的节点时,当前的L并不一定是这个合法节点的len值+1

    因为SAM上的节点的len值为其可以取得的长度的最大值,它有可能比上一次的L要大

    所以我的代码对这种情况进行了一下处理QAQ

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1100010;
    int n,m,cnt,la,len;
    char s[maxn];    
    struct Node{
    	int next[3];//0 1 2 
    	int len,link;
    }st[maxn<<1];
    int Q[maxn],h,t;
    int f[maxn];
    
    void init(){
    	cnt=la=0;
    	st[0].link=-1;
    }
    void add(int c){
    	int cur=++cnt;
    	st[cur].len=st[la].len+1;
    	int p;
    	for(p=la;p!=-1&&st[p].next[c]==0;p=st[p].link)st[p].next[c]=cur;
    	if(p==-1)st[cur].link=0;
    	else{
    		int q=st[p].next[c];
    		if(st[q].len==st[p].len+1)st[cur].link=q;
    		else{
    			int clone=++cnt;
    			st[clone]=st[q];
    			st[clone].len=st[p].len+1;
    			for(;p!=-1&&st[p].next[c]==q;p=st[p].link)st[p].next[c]=clone;
    			st[q].link=st[cur].link=clone;
    		}
    	}la=cur;
    }
    bool check(int k){
    	h=1;t=0;f[0]=0;
    	int p=0,L=0;
    	for(int i=1;i<=len;++i){
    		if(i>=k){
    			while(h<=t&&f[Q[t]]-Q[t]<f[i-k]-(i-k))t--;
    			Q[++t]=i-k;
    		}
    		int now=s[i]-'0';
    		int cur=p;
    		for(;p!=-1&&st[p].next[now]==0;p=st[p].link);
    		f[i]=f[i-1];
    		if(p==-1){p=0;L=0;continue;}
    		if(p==cur)L++;
    		else L=st[p].len+1;
    		p=st[p].next[now];
    		while(h<=t&&i-Q[h]>L)h++;
    		if(h<=t)f[i]=max(f[i],f[Q[h]]-Q[h]+i);
    	}return len*9<=f[len]*10;
    }
    
    int main(){
    	scanf("%d%d",&n,&m);init();
    	for(int i=1;i<=m;++i){
    		scanf("%s",s+1);len=strlen(s+1);
    		for(int j=1;j<=len;++j)add(s[j]-'0');
    		add(2);
    	}
    	while(n--){
    		scanf("%s",s+1);len=strlen(s+1);
    		int L=0,R=len;
    		while(L<R){
    			int mid=L+((R-L+1)>>1);
    			if(check(mid))L=mid;
    			else R=mid-1;
    		}printf("%d
    ",L);
    	}return 0;
    }
    

      

  • 相关阅读:
    mac 终端常见指令
    git常见指令
    iOS8的autolayout和size class
    UIWindow详解
    操作系统Unix、Windows、Mac OS、Linux的故事
    iOS引用当前显示的UIAlertView
    Unexpected CFBundleExecutable Key
    《CODE》讲了什么?
    exit和return的区别
    php 登录注册api接口代码
  • 原文地址:https://www.cnblogs.com/joyouth/p/5366996.html
Copyright © 2020-2023  润新知