首页 > 其他分享 >P4022 [CTSC2012]熟悉的文章 题解

P4022 [CTSC2012]熟悉的文章 题解

时间:2023-01-23 23:24:28浏览次数:54  
标签:include P4022 int 题解 num ans CTSC2012 now dp

题目链接

简要题意

给定 \(m\) 个模板串和 \(n\) 个匹配串,如果一个字符串是一个模板串的子串且长度不小于 \(L\) 则称其为“熟悉的”,对于每个匹配串,求一个最大的 \(L\),满足将匹配串分割,熟悉的子串的总长度大于原串长度的 \(90\%\)。

题目分析

首先对于 \(L\),如果有更大的 \(L\) 满足了它也一定满足,因此我们首先对其进行二分。

由于跟子串问题相关,且有多个模板串,我们根据模板串建出广义 SAM,分割这种方式有不可在某一位置重复的特点,因此可以考虑对其进行 DP,从前缀开始考虑,一遍 DP 一遍在 SAM 上匹配,求出当前前缀最长熟悉后缀。

设 \(f_i\) 表示割到 \(i\) 的最大熟悉长度,\(len_k\) 表示 SAM 上当前节点的最大长度,则有

\(f_i=\max(f_{i-1},f_j+(i-j)),j\in [i-len_k,i-L]\)

继续观察这个转移方程,由于我们是在 SAM 匹配,失败时往父节点走,\(i-len_k\) 递增,成功时不变,而 \(i-L\) 随 \(i\) 右移,因此决策集合只会往右增长,使用单调队列维护最优决策即可。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector> 
#include<queue>
#include<deque>
#define ll long long
using namespace std;
int tot=1,nex[3000001][2],le[3000001],fa[3000001];
int dp[3000001];
string s;
struct Node{
	int last,num;
};
queue<Node> q;
deque<Node> p;
void inserttrie(string s){
	int p=1,len=s.length();
	for(int i=0;i<len;i++){
		int num=s[i]-'0';
		if(!nex[p][num]) nex[p][num]=++tot;
		p=nex[p][num];
	}
}
int insertsam(int last,int num){
	int p=last,np=nex[last][num];le[np]=le[p]+1;
	p=fa[p];
	for(;p && !nex[p][num];p=fa[p]) nex[p][num]=np;
	if(!p) fa[np]=1;
	else{
		int q=nex[p][num];
		if(le[q]==le[p]+1) fa[np]=q;
		else{
			int clone=++tot;le[clone]=le[p]+1;
			for(int i=0;i<=1;i++)nex[clone][i]=(le[nex[q][i]]?nex[q][i]:0);
			for(;p && nex[p][num]==q;p=fa[p]) nex[p][num]=clone;
			fa[clone]=fa[q];fa[q]=fa[np]=clone;
		}
	}
	return np;
}
void build(){
	for(int i=0;i<=1;i++) if(nex[1][i]) q.push((Node){1,i});
	while(!q.empty()){
		int last=q.front().last,num=q.front().num;q.pop();
		int cur=insertsam(last,num);
		for(int i=0;i<=1;i++) if(nex[cur][i]) q.push((Node){cur,i});
	}
}
bool solve(string s,int ans){
	int len=s.length(),now=1,lth=0;
	for(int i=1;i<=len;i++);
	while(p.size()) p.pop_back();
	for(int i=0;i<len;i++){
		dp[i+1]=dp[i];
		int num=s[i]-'0';
		while(p.size() && (i+1-ans>=0 && dp[i+1-ans]-i-1+ans>p.front().last)){
			p.pop_front();
		}
		if(i+1-ans>=0) p.push_front((Node){dp[i+1-ans]-i-1+ans,i+1-ans});
		while(!nex[now][num]){
			now=fa[now];
			lth=le[now];
		}
		if(!now){now=1;lth=0;continue;}
		now=nex[now][num];lth++;
		while(p.size() && i+1-lth>p.back().num) p.pop_back();
		if(p.size())dp[i+1]=max(dp[i+1],p.back().last+i+1);
	}
	double num=len*1.0/10*9;
	if(num<=dp[len]) return true;
	else return false;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>s;
		inserttrie(s);
	}
	build();
	for(int i=1;i<=n;i++){
		cin>>s;
		int l=1,r=s.length();
		while(l<r){
			int mid=(l+r+1)>>1;
			if(solve(s,mid)) l=mid;
			else r=mid-1;
		}
		cout<<l<<endl;
	}
}

标签:include,P4022,int,题解,num,ans,CTSC2012,now,dp
From: https://www.cnblogs.com/eastcloud/p/17065667.html

相关文章

  • 程序员经典问题解答
    帮助在学习、上班的过程中,你是否经常遇到疑难问题无法解决,为此备受折磨?别担心,小编精选多道程序员最头痛的技术问题予以回答。QA小伙伴程序大牛C语言 Q:如何引用一个已经定义......
  • Solution 题解 UVA1389 Hard Life: 最小割,有向图,分数规划,和牛顿迭代
    题解UVA1389HardLife:最小割,有向图,分数规划,和牛顿迭代Preface黑题好耶看到了题解里面大多数是二分,我就来讲一讲简单又快速的DinkelbachAlgorithm吧!0-1分数规划......
  • 洛谷P2036 PERKET题解
     先来审题,主要有以下几个条件:酸度求乘积,苦度求和,两者相减的值最小(当然是绝对值)。下面附上AC代码:#include<bits/stdc++.h>//万能头文件usingnamespacestd;//......
  • 【题解】CF193D Two Segments
    题意给定一个\(1\simN\)的排列,在这个排列中选出两段互不重叠的区间,求使选出的元素排序后构成公差为1的等差数列的方案数。选出的两段区间中元素构成的集合相同时视为同一......
  • 【题解】P5787 二分图 /【模板】线段树分治
    概念线段树分治是一种用于维护时间轴等的离线算法,本质上是通过维护时间轴的连续区间得到某一时刻的状态。时间复杂度和普通线段树相同,空间复杂度为\(O(n\logn)\)例题......
  • Codeforces Round #845 (Div. 2) D题解
    D.ScoreofaTree题目链接:https://codeforces.com/contest/1777/problem/D个人感觉还是比较简单的一道计数题题意是给你一颗有n(n<=2e5)节点的树,初始时每个节点有一个......
  • 【题解】P4755 Beautiful Pair
    麻了,这么多典题没做过……思路分治/笛卡尔树。这一类和区间最值相关的区间端点对计数应该都可以用这种做法做。由于求的是最大值,不妨从大到小考虑每个\(a_i\)的贡......
  • 【题解】Codeforces Round #845 (Div. 2) and ByteRace 2023
    建议开题顺序:A->B->C->F->E->D诈骗差评,典题差评,想易写难数据结构差评。A.EverybodyLikesGoodArrays!好像是结论题,但是一力降十会。显然最后合并完后,每个......
  • P5030 题解
    前言题目传送门!更好的阅读体验?一道没啥意思的题目,但是好像很多题解都过不了现在的数据?思路只不过是把正常题目的马(\(1,2\))换成了另一种东西(\(1,3\))。很套路地,黑白......
  • 洛谷 P1123 取数游戏 (又是写了好久 最后还是无奈看了题解……)
    对于这个题感觉是一个比较典型的dfs.本题的状态是对于每个数字你可以选也可以不选,但是一旦你选定某个数字之后,他会对其周围的数字产生影响所以一定要标记好(注意这里标记的......