洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)
我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解……
首先,对$S$和每一次的$T$都建一个SAM
先考虑一下$l=1,r=\left| S \right|$的情况
设$lim_i$表示字符串$T[1..i]$能在$S$中匹配到的最长后缀(即$T[i-lim_i+1,i]$是$S$的子串且$lim_i$最大)(有可能不存在这个字符那么$lim_i=0$)
这个$lim_i$可以不断地在$S$的后缀自动机上跳来求出。当无法向下匹配时,一直跳parent树直到可以匹配为止
我们假设对于$T$的后缀自动机,每一个节点的$endpos$集合中所能代表的最长的字符串长度为$l_i$,$tag_i$表示该集合字符串第一次出现的结尾位置(因为集合里字符串互为后缀所以结尾相同),$fa_i$表示parent树上的父亲,$cnt$表示自动机节点总个数
那么答案就是$$ans=\sum_{i=2}^{cnt}max(0,l_i-max(l_{fa_i},lim_{tag_i}))$$
ps:这里的lim指的并不是上文的lim而是最长后缀的长度
上面式子的意思是,对于每一个节点,它不属于$S$的子串的总个数为当前节点代表的集合字符串个数减去与$S$有匹配的子串个数
然后只要在$T$的后缀自动机上枚举每一个节点就可以了
现在来考虑$l$和$r$任意的情况该怎么做
这个时候就要用线段树维护后缀自动机的$endpos$集合了(不明白这个怎么做的我简单说一下,就是搞一个动态开点线段树,如果一个节点的$endpos$集合里有某一个位置就把它加入以该点为根的树中,parent树上父亲节点的$endpos$集合必然包含儿子的$endpos$集合所以将每个点的$endpos$集合与它儿子的合并。然后查询这个节点是否有$endpos$位于某个区间中只要在线段树上查询看看这个区间代表的节点是否被开出来过就好了(因为线段树上只有存在的位置的节点被开出来过))
我们在处理$lim_i$集合的时候要注意,要判断当前节点是否在$S[l..r]$区间中出现过。设$p$为当前在$S$的自动机上跑到的节点,$len$表示匹配了$[i-len+1..i]$,因为要看能否转移到下一个节点,所以要匹配$[i-len,i]$,且$[l+len,r]$中有后继节点的$endpos$存在才行(可能说的烦了点,仔细想想为什么)
差不多就这样
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(ll x){
if(C><<)Ot();
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1e6+,M=2e7+,inf=0x3f3f3f3f;
int q,n,m,rt[N],lim[N];char S[N];ll ans;
namespace tree{
int cnt,L[M],R[M];
void ins(int &p,int l,int r,int x){
if(!p) p=++cnt;
if(l==r) return;
int mid=(l+r)>>;
if(x<=mid) ins(L[p],l,mid,x);
else ins(R[p],mid+,r,x);
}
int merge(int x,int y){
if(!x||!y) return x+y;
int p=++cnt;
L[p]=merge(L[x],L[y]);
R[p]=merge(R[x],R[y]);
return p;
}
bool query(int p,int l,int r,int ql,int qr){
if(!p||ql>qr) return false;
if(ql<=l&&qr>=r) return true;
int mid=(l+r)>>;
if(ql<=mid&&query(L[p],l,mid,ql,qr)) return true;
if(qr>mid&&query(R[p],mid+,r,ql,qr)) return true;
return false;
}
}
namespace SAM{
int cnt=,last=;
int ch[N][],l[N],fa[N],c[N],a[N],in[N];
void ins(int c){
int p=last,np=++cnt;last=np,l[np]=l[p]+,in[np]=;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=;
else{
int q=ch[p][c];
if(l[q]==l[p]+) fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+;
memcpy(ch[nq],ch[q],sizeof(int)*());
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
inline void calc(){
for(int i=;i<=n;++i) ins(S[i]-'a');
for(int i=;i<=cnt;++i) ++c[l[i]];
for(int i=;i<=cnt;++i) c[i]+=c[i-];
for(int i=;i<=cnt;++i) a[c[l[i]]--]=i;
for(int i=cnt,p;i;--i){
p=a[i];
if(in[p]) tree::ins(rt[p],,n,l[p]);
rt[fa[p]]=tree::merge(rt[fa[p]],rt[p]);
}
}
}
namespace solve{
int cnt=,last=;
int ch[N][],l[N],fa[N],c[N],a[N],tag[N];
inline void init(){
cnt=last=,memset(ch[],,sizeof(int)*());
}
inline int newnode(){
++cnt;memset(ch[cnt],,sizeof(int)*());return cnt;
}
void ins(int c){
int p=last,np=newnode();last=np,tag[np]=l[np]=l[p]+;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=;
else{
int q=ch[p][c];
if(l[q]==l[p]+) fa[np]=q;
else{
int nq=newnode();l[nq]=l[p]+,tag[nq]=tag[q];
memcpy(ch[nq],ch[q],sizeof(int)*());
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
ll solve(){
int L,R;
scanf("%s%d%d",S+,&L,&R);
init();
m=strlen(S+);
for(int len=,p=,i=;i<=m;++i){
int c=S[i]-'a';
ins(c);
while(true){
if(SAM::ch[p][c]&&tree::query(rt[SAM::ch[p][c]],,n,L+len,R)){
++len,p=SAM::ch[p][c];break;
}
if(len==) break;
--len;
if(len==SAM::l[SAM::fa[p]]) p=SAM::fa[p];
}
lim[i]=len;
}
ans=;
for(int i=;i<=cnt;++i)
ans+=max(,l[i]-max(l[fa[i]],lim[tag[i]]));
return ans;
}
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%s",S+);
n=strlen(S+);
SAM::calc();
scanf("%d",&q);
while(q--) print(solve::solve());
Ot();
return ;
}
洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)的更多相关文章
- 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
- BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)
LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...
- [NOI2018]你的名字(后缀自动机+线段树)
题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...
- luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并
其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...
- UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)
NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...
- NOI 2018 你的名字 (后缀自动机+线段树合并)
题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...
- 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)
题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...
随机推荐
- Machine Learning No.4: Regularization
1. Underfit = High bias Overfit = High varience 2. Addressing overfitting: (1) reduce number of feat ...
- 安装wampserver 计算机丢失msvcr100.dll
刚刚重新安装了Windows 7 64位系统,再安装Wampserver 2时却提示系统错误,如下图所示: 在网上下载了MSVCR100.dll放到system32文件夹下依然没有用. 百度搜索了一下 ...
- jumpserver v3.0
文档地址 www.php230.com/weixin1451347094.html
- apace搭建站点
Listen 127.0.0.1:3310<VirtualHost *:3306> ServerName 127.0.0.1:3306 DocumentRoot "F:/Baid ...
- 京东面试题 Java相关
1.JVM的内存结构和管理机制: JVM实例:一个独立运行的java程序,是进程级别 JVM执行引擎:用户运行程序的线程,是JVM实例的一部分 JVM实例的诞生 当启动一个java程序时.一个JVM实 ...
- 搭建自己的AppRTCDemo服务器
http://www.jianshu.com/p/c55ecf5a3fcf http://io.diveinedu.com/2015/02/05/%E7%AC%AC%E5%85%AD%E7%AB%A0 ...
- eclipse如何在不联网的情况下引入dtd约束文件
1. 获取dtd文件,解压 F:\Java配置文件\Mybatis\mybatis-3.3.0\mybatis-3.3.0.jar\org\apache\ibatis\builder\xml\ 路径下 ...
- linux svn yum 安装、开机自启动
1.查询是否安装 rpm -qa subversion
- ls命令还能这么玩
排序文件大小: 我们希望以文件大小排序,我们可以使用-S 参数来这么做 如果希望文件大小从小到大排序: 如果只希望列出目录条目: 增加 /(斜线) 标记目录:要这么做,使用-p选项: 通过修改时间列出 ...
- HDU5692 Snacks
HDU5692 Snacks Problem Description 百度科技园内有n个零食机,零食机之间通过n−1条路相互连通.每个零食机都有一个值v,表示为小度熊提供零食的价值. 由于零食被频繁的 ...