• [BZOJ 2806][Ctsc2012]Cheat


    [BZOJ 2806][Ctsc2012]Cheat

    题意

    给定一个 (m) 个串的字典, 对于 (n) 组查询串, 求一个最大的 (L_0) 使查询串能被拆分为若干子串使得其中是字典串的子串且长度不小于 (L_0) 的子串的长度之和不小于总长的 (90\%).

    所有串均为01串, 输入文件大小不超过 (110000 exttt{B}).

    题解

    我可能在做这题前对SAM的理解有点偏差

    首先不难看出这个 (L_0) 是可以二分的.

    若查询串的一个子串同时也是字典中某个串的子串, 我们称之为有效子串.

    然后我们尝试DP求出最优情况下的有效子串总长. 设 (dp_i) 表示长度为 (i) 的前缀中的有效子串总长的最大值. 我们可以枚举最后一个有效串的长度进行转移, 同时这一步还可以不选择任何子串 (即字符 (i) 不包含在任何有效子串中, 不做任何贡献地从 (dp_{i-1}) 转移). 于是我们需要快速知道以 (i) 为右端点的最长有效子串长度.

    显然这一步我们可以对字典串建立广义SAM求出. 具体做法是把查询串丢到广义SAM上运行, 运行时如果能匹配就按匹配边走并产生 (+1) 贡献, 否则跳 (prt) 并将当前匹配长度更新为 (prt)(len/max) 长.

    但是这样枚举长度转移复杂度可能是 (O(|S|^2)) 的.

    容易发现每次 (i) 增加 (1) 后只会多一个决策点 (i-L_0). 且若决策集合中存在 (j<i) 满足 (dp_j + (i-j) le dp_i) , 则 (dp_j) 必定不优秀, 单调队列一下就可以做到 (O(n)) 了.

    代码实现

    #include <bits/stdc++.h>
    
    const int MAXN=2e6+10;
    
    int n;
    int m;
    int cnt=1;
    int last=1;
    int root=1;
    int dp[MAXN];
    int prt[MAXN];
    int len[MAXN];
    char buf[MAXN];
    int chd[MAXN][2];
    
    void Extend(int);
    bool Check(int,int);
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<m;i++){
    		last=root;
    		scanf("%s",buf);
    		for(char* p=buf;*p!='';p++)
    			Extend(*p-'0');
    	}
    	for(int i=0;i<n;i++){
    		scanf("%s",buf+1);
    		int len=strlen(buf+1);
    		int l=0,r=len+1;
    		while(r-l>1){
    			int mid=(l+r)>>1;
    			if(Check(mid,len))
    				l=mid;
    			else
    				r=mid;
    		}
    		printf("%d
    ",l);
    	}
    	return 0;
    }
    
    bool Check(int L,int n){
    //	printf("L=%d,n=%d
    ",L,n);
    	int clen=0;
    	int cur=root;
    	std::deque<int> q;
    	for(int i=1;i<=n;i++){
    		dp[i]=dp[i-1];
    		int x=buf[i]-'0';
    		while(cur!=root&&!chd[cur][x]){
    			cur=prt[cur];
    			clen=len[cur];
    		}
    		if(chd[cur][x]){
    			++clen;
    			cur=chd[cur][x];
    		}
    		while(!q.empty()&&dp[q.back()]-q.back()<=dp[i-L]-(i-L))
    			q.pop_back();
    		q.push_back(i-L);
    		while(!q.empty()&&q.front()<i-clen)
    			q.pop_front();
    		if(!q.empty())
    			dp[i]=std::max(dp[i],dp[q.front()]+i-q.front());
    	}
    	return dp[n]*10>=n*9;
    }
    
    void Extend(int x){
    	int p=last;
    	int np=++cnt;
    	len[last=np]=len[p]+1;
    	while(p&&chd[p][x]==0)
    		chd[p][x]=np,p=prt[p];
    	if(!p)
    		prt[np]=root;
    	else{
    		int q=chd[p][x];
    		if(len[q]==len[p]+1)
    			prt[np]=q;
    		else{
    			int nq=++cnt;
    			memcpy(chd[nq],chd[q],sizeof(chd[q]));
    			prt[nq]=prt[q];
    			prt[q]=nq;
    			prt[np]=nq;
    			len[nq]=len[p]+1;
    			while(p&&chd[p][x]==q)
    				chd[p][x]=nq,p=prt[p];
    		}
    	}
    }
    
    

  • 相关阅读:
    DL_WITH_PY系统学习(第2章)
    JavaScript学习require的用法
    Vue的prop属性
    vue store action与Mutation与getters的具体用法
    OpenEBS 实现 Local PV 动态持久化存储
    vue modules 使用
    【转载】 tSNE是什么? —— 使用指南
    【转载】 Do's and Don'ts of using tSNE to Understand Vision Models —— tSNE 作者写的使用指南(PPT版本)
    【转载】 机器学习数据可视化 (tSNE 使用指南)—— Why You Are Using tSNE Wrong
    【转载】 机器学习的高维数据可视化技术(tSNE 介绍) 外文博客原文:How tSNE works and Dimensionality Reduction
  • 原文地址:https://www.cnblogs.com/rvalue/p/10498477.html
Copyright © 2020-2023  润新知