【BZOJ3413】匹配(后缀自动机,线段树合并)

题面

BZOJ

题解

很好的一道题目。

做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和。

首先是空前缀的出现次数,意味着你会去匹配第一个字符。

然后是第一个字符的出现次数,意味着你回去匹配前两个字符。

如此下去就是最后的答案。

那么构建\(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】匹配(后缀自动机,线段树合并)的更多相关文章

  1. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  2. BZOJ 3413 匹配 (后缀自动机+线段树合并)

    题目大意: 懒得概括了 神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题 线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个 ...

  3. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  4. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  5. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  6. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  7. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  8. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  9. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

  10. NOI 2018 你的名字 (后缀自动机+线段树合并)

    题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...

随机推荐

  1. 2019 The 19th Zhejiang University Programming Contest

    感想: 今天三个人的状态比昨天计院校赛的状态要好很多,然而三个人都慢热体质导致签到题wa了很多发.最后虽然跟大家题数一样(6题),然而输在罚时. 只能说,水题还是刷得少,看到签到都没灵感实在不应该. ...

  2. MongoDB的导入导出

    一:MongoDB的导出功能 mongodb提供了导入和导出的功能,分别是MongoDB下载目录下的mongoexport.exe和mongoimport.exe文件 ,具体的导出命令格式如下: mo ...

  3. 通过git命令行从github或服务器上克隆、修改和更新项目

    项目开发时,为了方便版本管理,许多公司采用git来控制项目版本.简单介绍下: 第一步:在本地新建一个文件夹,作为本地仓库,如“test”.打开git bash,进入到该文件夹目录下,如下图: 第二步: ...

  4. Android恶意样本数据集汇总

    硕士论文的研究方向为Android恶意应用分类,因此花了一点时间去搜集Android恶意样本.其中一部分来自过去论文的公开数据集,一部分来自社区或平台的样本.现做一个汇总,标明了样本或数据集的采集时间 ...

  5. 第三次作业 (一)----------------------Visual Studio 2015的安装及单元测试

    这是第三周的第一个作业,Visual Studio 2015的安装及单元测试. 我的电脑之前安装过Visual Studio 2015,但是在安装过程中我从来没有留意过各种注意事项,所集正好借此作业的 ...

  6. html转js字符串拼接

    https://www.bejson.com/convert/html_js/ html转js字符串拼接

  7. 12.10 Daily Scrum

    各种大作业,进度会放缓一些.   Today's Task Tomorrow's Task 丁辛 完善餐厅列表,显示距离. 实现和菜谱相关的餐厅列表.             邓亚梅          ...

  8. BF算法和KMP算法 python实现

    BF算法 def Index(s1,s2,pos = 0): """ BF算法 """ i = pos j = 0 while(i < ...

  9. socket.io框架

    socket.io框架 一.问题背景 目前公司在互联网产品上需要程序与前端部分要进行一个实时交互,在进行一定程度上的选型后,决定使用socket.io框架进行一个实践,算是公司的一个新的 尝试,也算是 ...

  10. JSON数据格式解析

    JSON数据的语法规则 1.数据以键值对的形式 2.数据由逗号分隔 3.花括号保存对象 4.方括号保存数组 以PHP的数组为例: <?php $arr = array( "aaaa&q ...