【BZOJ3413】匹配(后缀自动机,线段树合并)
【BZOJ3413】匹配(后缀自动机,线段树合并)
题面
题解
很好的一道题目。
做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和。
首先是空前缀的出现次数,意味着你会去匹配第一个字符。
然后是第一个字符的出现次数,意味着你回去匹配前两个字符。
如此下去就是最后的答案。
那么构建\(SAM\)后线段树合并维护好每个点的\(endpos\)。
然后对于询问串在\(SAM\)上跑一遍就好了。
注意下每个\(endpos\)的可行范围到底是哪里,以及最终整个询问串是不需要计算到答案里的。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 100100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,m;
char ch[MAX];
struct SegNode{int ls,rs,v;}T[MAX<<6];
int TOT,rt[MAX<<1],lst[MAX<<1];
void Modify(int &x,int l,int r,int p)
{
	if(!x)x=++TOT;T[x].v+=1;if(l==r)return;
	int mid=(l+r)>>1;
	if(p<=mid)Modify(T[x].ls,l,mid,p);
	else Modify(T[x].rs,mid+1,r,p);
}
int Merge(int x,int y)
{
	if(!x||!y)return x|y;
	int z=++TOT;
	T[z].ls=Merge(T[x].ls,T[y].ls);
	T[z].rs=Merge(T[x].rs,T[y].rs);
	T[z].v=T[T[z].ls].v+T[T[z].rs].v;
	return z;
}
int Query(int x,int l,int r,int L,int R)
{
	if(L>R||!x)return 0;
	if(L<=l&&r<=R)return T[x].v;
	int mid=(l+r)>>1,ret=0;
	if(L<=mid)ret+=Query(T[x].ls,l,mid,L,R);
	if(R>mid)ret+=Query(T[x].rs,mid+1,r,L,R);
	return ret;
}
struct Node
{
	int son[10];
	int len,ff;
}t[MAX<<1];
int last=1,tot=1;
void extend(int c,int id)
{
	int p=last,np=++tot;last=tot;
	t[np].len=t[p].len+1;
	while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
	if(!p)t[np].ff=1;
	else
	{
		int q=t[p].son[c];
		if(t[q].len==t[p].len+1)t[np].ff=q;
		else
		{
			int nq=++tot;
			t[nq]=t[q];t[nq].len=t[p].len+1;
			t[q].ff=t[np].ff=nq;
			while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
		}
	}
	Modify(rt[np],1,n,id);lst[np]=id;
}
int p[MAX<<1],a[MAX<<1];
int check(char *ch)
{
	int now=1,l=strlen(ch+1);
	for(int i=1;i<=l;++i)
	{
		int c=ch[i]-48;
		if(t[now].son[c])now=t[now].son[c];
		else return -1;
	}
	return lst[now];
}
int main()
{
	n=read();scanf("%s",ch+1);memset(lst,63,sizeof(lst));
	for(int i=1;i<=n;++i)extend(ch[i]-48,i);
	for(int i=1;i<=tot;++i)a[t[i].len]++;
	for(int i=1;i<=n;++i)a[i]+=a[i-1];
	for(int i=1;i<=tot;++i)p[a[t[i].len]--]=i;
	for(int i=tot;i;--i)
		if(t[p[i]].ff)
		{
			rt[t[p[i]].ff]=Merge(rt[t[p[i]].ff],rt[p[i]]);
			lst[t[p[i]].ff]=min(lst[t[p[i]].ff],lst[p[i]]);
		}
	m=read();
	while(m--)
	{
		scanf("%s",ch+1);
		int l=strlen(ch+1),h=check(ch),ans;
		if(h==-1)ans=n;
		else ans=h+1-l;
		for(int i=1,now=1;i<l;++i)
		{
			int c=ch[i]-48;
			if(t[now].son[c])now=t[now].son[c];
			else break;
			if(h==-1)ans+=Query(rt[now],1,n,1,n);
			else ans+=Query(rt[now],1,n,1,h-l+i);
		}
		printf("%d\n",ans);
	}
	return 0;
}
【BZOJ3413】匹配(后缀自动机,线段树合并)的更多相关文章
- BZOJ3413: 匹配(后缀自动机 线段树合并)
		题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ... 
- BZOJ 3413 匹配 (后缀自动机+线段树合并)
		题目大意: 懒得概括了 神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题 线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个 ... 
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
		题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ... 
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
		bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ... 
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
		https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ... 
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
		模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ... 
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
		点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ... 
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
		题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ... 
- CF 666E Forensic Examination——广义后缀自动机+线段树合并
		题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ... 
- NOI 2018 你的名字 (后缀自动机+线段树合并)
		题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ... 
随机推荐
- 解决PowerDesigner 16 Generate Datebase For Sql2005/2008 对象名sysproperties无效的问题
			在PowerDesigner 16 中生成的sql语句,在执行的时候报错:对象名sysproperties 无效的错误;造成此问题的原因是由于Sql 2005.2008 删除了系统表 sysprope ... 
- 微信小程序之wx.request:fail错误,真机预览请求无效问题解决,安卓,ios网络预览异常
			新版开发者工具增加了https检查功能:可使用此功能直接检查排查ssl协议版本问题: 可能原因:0:后台域名没有配置0.1:域名不支持https1:没有重启工具:2:域名没有备案,或是备案后不足24小 ... 
- 给 MSYS2 添加中科大的源
			最近一段时间不知怎么的,使用默认的 MSYS2 源升级软件或是安装新软件的特别的慢.所以就翻了翻国内的几个开源软件的镜像库,发现中科大的库里就有 MSYS2.所以就研究了一下,给 MSYS2 添加了中 ... 
- MySQL高可用方案MHA在线切换的步骤及原理
			在日常工作中,会碰到如下的场景,如mysql数据库升级,主服务器硬件升级等,这个时候就需要将写操作切换到另外一台服务器上,那么如何进行在线切换呢?同时,要求切换过程短,对业务的影响比较小. MHA就提 ... 
- Shell学习笔记二
			一.调试脚本 调试功能是每一种编程语言都应该实现的重要特性之一,当出现一些始料未及的情况时,用它来生成脚本运行信息.调试信息可以帮你弄清楚是什么原因使得程序发生崩溃或行为异常.每位系统程序员都应该了解 ... 
- 《Multiplayer Game Programming》阅读笔记
			在图书馆发现一本<网络多人游戏架构与编程>-- Joshua Glazer, Sanjay Madhav 著.书挺新的,17年出版的,内容很有趣,翻一翻可以学到不少在<计算机网络&g ... 
- 开源通用爬虫框架YayCrawler-开篇
			各位好!从今天起,我将用几个篇幅的文字向大家介绍一下我的一个开源作品--YayCrawler,其在GitHub上的网址是:https://github.com/liushuishang/YayCraw ... 
- Maven -Maven配置tomcat插件 两种
			Maven Tomcat插件现在主要有两个版本,tomcat-maven-plugin和tomcat7-maven-plugin,使用方式基本相同. tomcat-maven-plugin 插件官网: ... 
- VMware 桥接 Bridge 复制物理网络连接状态
			https://zhidao.baidu.com/question/535593443.html 意思就是说,VM上使用的是虚拟的网卡,也就是说VM虚拟机上的网卡不是真实存在的,而桥接还有其他的网路链 ... 
- [读书笔记]Linux命令行与shell编程读书笔记04 安装软件,编辑器注意事项
			1. debian以及redhat两种主流的linux发行版用的包管理工具 debian的包管理工具是 dpkg 再现安装的是 apt apt的工具主要有 apt-get apt-cache apti ... 
