【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. odoo 10.0部署shell

    环境ubuntu16+nginx+python2.7.12+postgresql9.5+odoo 10.0 community #!/bin/bash #author:guoyihot@outlook ...

  2. redis 配置 架构 基础

    redis 官网  redis.io   io为某国家域名后缀 有redis各种版本. java 版本 又分各种工具 clients 下 RedisClient为图形化管理界面 Jedis 才是jav ...

  3. NOI Day2线上同步赛崩盘记

    Preface 蒟蒻愉快的NOI线上赛Day2之行,不过因为太菜就凉了 这次由于策略&&网络的问题,最后两题都没有交,结果就靠T1稳住拿了75分就回家了. 我真是太菜了. 屠龙勇士 首 ...

  4. [Spark][Python]对HDFS 上的文件,采用绝对路径,来读取获得 RDD

    对HDFS 上的文件,采用绝对路径,来读取获得 RDD: In [102]: mydata=sc.textFile("file:/home/training/test.txt")1 ...

  5. 一致性哈希(hash)算法

    一.算法背景 一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正 ...

  6. Substrings Sort

    You are given nn strings. Each string consists of lowercase English letters. Rearrange (reorder) the ...

  7. Popush End

    coconut: (咳咳)作为一名后台开发者,我觉得自己在这次作业完成中最大的收获就是跟node.js的异步模型打交道.首先我得出了一个这样的结论:异步模型能够提高服务器的高性能并发请求,但是却加大了 ...

  8. 软件工程——移动的HelloWorld

    package disiti;       import java.awt.Color;   import java.awt.Cursor;   import java.awt.Font;   imp ...

  9. QT 窗口置顶功能

    Qt中,保持窗口置顶的设置为: Qt::WindowFlags m_flags = windowFlags(); setWindowFlags(m_flags | Qt::WindowStaysOnT ...

  10. 01springboot快速入门

    SpringBoot快速入门 springboot的宗旨是习惯大于配置,所以spring里面大量使用了默认的配置来简化spring的配置.spring Boot的主要优点: 为所有Spring开发者更 ...