题意

不妨先考虑\(l=1,r=|S|\)的情况:

这时我们要求的其实是\(S,T\)的本质不同的公共子串数量。

首先对\(S\)建一个后缀自动机,同时对于每个\(T\),我们也建一个自动机。

根据后缀自动机的性质,后缀自动机的所有节点的代表的字符串的集合代表了\(T\)的全部子串,因此我们可以考虑\(T\)后缀自动机的上的每一个点代表的字符串中有多少是和\(S\)的公共子串。

于是考虑怎么求这个东西:

我们对于\(T\)的每一个前缀\(T[1...i]\)求出\(match_i\)表示这个前缀的后缀能跟\(S\)匹配的最长长度,这个东西可以在\(S\)的后缀自动机上匹配\(T\)来求出,见这题

对于一个节点\(x\),我们设它的字符串长度范围为\([minlen_x,maxlen_x]\),\(endpos\)集合的第一个为\(firpos_x\)。

这时我们发现这个节点长度在\(match_{firpos_x}+1\)往上的字符串都不可能和\(S\)匹配,因此这个节点的贡献为\(\max(len_x-\max(len_{fa_x},match_{firpos_x}),0)\)。

现在考虑\([l,r]\)的限制,我们按照套路用线段树维护\(S\)的后缀自动机每个节点\(endpos\)集合,求\(match\)的时候只走合法的点即可。

求match时注意个细节,写在注释里了。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int n,m,Q,tot,cnt_edge;
int head[maxn<<1],root[maxn<<1],match[maxn];
char s[maxn];
struct edge{int to,nxt;}e[maxn<<2];
struct Seg
{
#define lc(p) (seg[p].lc)
#define rc(p) (seg[p].rc)
#define sum(p) (seg[p].sum)
int lc,rc,sum;
}seg[maxn*50];
inline void add_edge(int u,int v)
{
e[++cnt_edge].nxt=head[u];
head[u]=cnt_edge;
e[cnt_edge].to=v;
}
void insert(int &p,int l,int r,int pos)
{
if(!p)p=++tot;
sum(p)++;
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)insert(lc(p),l,mid,pos);
else insert(rc(p),mid+1,r,pos);
}
int merge(int p,int q,int l,int r)
{
if(!p||!q)return p+q;
int x=++tot;sum(x)=sum(p)+sum(q);
if(l==r)return x;
int mid=(l+r)>>1;
lc(x)=merge(lc(p),lc(q),l,mid);
rc(x)=merge(rc(p),rc(q),mid+1,r);
return x;
}
int query(int p,int l,int r,int ql,int qr)
{
if(!p)return 0;
if(l>=ql&&r<=qr)return sum(p);
int mid=(l+r)>>1,res=0;
if(ql<=mid)res+=query(lc(p),l,mid,ql,qr);
if(qr>mid)res+=query(rc(p),mid+1,r,ql,qr);
return res;
}
void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
dfs(e[i].to),root[x]=merge(root[x],root[e[i].to],1,n);
}
struct SAM
{
int tot,last;
int fa[maxn<<1],len[maxn<<1],firpos[maxn<<1];
int ch[maxn<<1][30];
inline void clear()
{
for(int i=1;i<=tot;i++)
{
fa[i]=len[i]=firpos[i]=0;
memset(ch[i],0,sizeof(ch[i]));
}
last=tot=1;
}
inline void add(int c,int id)
{
int now=++tot;len[now]=len[last]+1;firpos[now]=id;
int p=last;last=now;
while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
if(!p){fa[now]=1;return;}
int q=ch[p][c];
if(len[q]==len[p]+1)fa[now]=q;
else
{
int nowq=++tot;len[nowq]=len[p]+1;firpos[nowq]=firpos[q];
memcpy(ch[nowq],ch[q],sizeof(ch[q]));
fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
}
}
}sam1,sam2;
inline int read()
{
char c=getchar();int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline void getmatch(char* s,int l,int r)
{
int len=strlen(s+1),now=1,nowl=0;
for(int i=1;i<=len;i++)
{
while(2333)
{
if(sam1.ch[now][s[i]-'a']&&query(root[sam1.ch[now][s[i]-'a']],1,n,l+nowl,r))//注意是l+nowl,这是endpos的集合。
{
now=sam1.ch[now][s[i]-'a'],nowl++;
break;
}
if(!nowl)break;
nowl--;//注意不要直接跳fa[now],因为区间缩小可能产生答案。
if(nowl==sam1.len[sam1.fa[now]])now=sam1.fa[now];
}
match[i]=nowl;
}
}
int main()
{
scanf("%s",s+1);n=strlen(s+1);
sam1.clear();
for(int i=1;i<=n;i++)sam1.add(s[i]-'a',i),insert(root[sam1.last],1,n,i);
for(int i=2;i<=sam1.tot;i++)add_edge(sam1.fa[i],i);
dfs(1);
Q=read();
while(Q--)
{
ll ans=0;
scanf("%s",s+1);m=strlen(s+1);
int l=read(),r=read();
sam2.clear();
for(int i=1;i<=m;i++)sam2.add(s[i]-'a',i);
getmatch(s,l,r);
for(int i=2;i<=sam2.tot;i++)
ans+=max(0,sam2.len[i]-max(match[sam2.firpos[i]],sam2.len[sam2.fa[i]]));
printf("%lld\n",ans);
}
return 0;
}

luoguP4770 [NOI2018]你的名字的更多相关文章

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

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

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

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

  3. 【BZOJ5417】[NOI2018]你的名字(线段树,后缀自动机)

    [BZOJ5417][NOI2018]你的名字(线段树,后缀自动机) 题面 BZOJ 洛谷 题解 首先考虑\(l=1,r=|S|\)的做法,对于每次询问的\(T\)串,暴力在\(S\)串的\(SAM\ ...

  4. 【LuoguP4770】[NOI2018] 你的名字

    题目链接 题意简述 给定一个串 \(S\) 多组询问 , 每次给定一个串 \(T\) 和一个 区间 \([l,r]\) 求串\(T\) 有多少个本质不同的子串 满足不是 \(S[l...r]\) 的子 ...

  5. [NOI2018]你的名字(后缀自动机+线段树)

    题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...

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

    看到题目名字去补番是种怎么样的体验 我只会 \(68\) 分,打了个暴力.正解看了一会儿,发现跟 \([HEOI2016/TJOI2016]\) 字符串很像,用线段树合并维护 \(endpos\) 集 ...

  7. [BZOJ5417] [NOI2018]你的名字

    Description 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的, ...

  8. [NOI2018]你的名字(68pts) 后缀自动机

    讲解在满分做法的博客中 Code: #include <cstdio> #include <algorithm> #include <cstring> #defin ...

  9. UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

    NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...

随机推荐

  1. poj 1182 食物链 并查集 题解《挑战程序设计竞赛》

    地址 http://poj.org/problem?id=1182 题解 可以考虑使用并查集解决 但是并不是简单的记录是否同一组的这般使用 每个动物都有三个并查集 自己 天敌 捕食 并查集 那么在获得 ...

  2. 【转】bootstrap table轻松实现数据表格

    在使用bootstrap table时可能在很多时候回用的表格来显示数据,如果自己写那肯定没问题,但是数据展示出来就麻烦多了,然而bootstrap table 封装了一套完善的数据表格组件,把从后台 ...

  3. 怎么解决 hash 冲突

    开放定址法: 线性探测再散列 二次探测再散列 伪随机 再hash: 同时构造,多个不同的hash函数 链地址: 链表, 建立公共溢出区: 分为基本表和溢出表两个部分 开放散列(open hashing ...

  4. 【Oracle】SQL的各种连接join

    SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段. 最常见的 JOIN 类型: INNER JOIN(简单的 JOIN).LEFT JOIN.RIGHT JOIN.F ...

  5. 图书分享 -《Natural Language Processing with Python》

    -<Natural Language Processing with Python> 链接:https://pan.baidu.com/s/1_oalRiUEw6bXbm2dy5q_0Q ...

  6. js中的NaN,isNaN与Number.isNaN的区别,如何判断一个值严格等于NaN

    在JavaScript的数字类型Number中,我们最常使用的大概是整数类型与浮点数类型,但除这两者外,还有个特殊的存在NaN,为什么NaN!==NaN?我们如何判断一个值是否等于NaN呢?这篇文章好 ...

  7. vue项目里面使用脚手架实现跨域

    今天在做vue项目的时候,项目在本地,接口数据在阿里云,这就造成了跨域,在网上找了好久,网上大部分的方法都是找到config文件夹下面的index进行修改的,可是我找到的Index却和他们描述的不一样 ...

  8. 前端笔记之React(七)redux-saga&Dva&路由

    一.redux-saga解决异步 redux-thunk 和 redux-saga 使用redux它们是必选的,二选一,它们两个都可以很好的实现一些复杂情况下redux,本质都是为了解决异步actio ...

  9. 【HDU6037】Expectation Division(动态规划,搜索)

    [HDU6037]Expectation Division(动态规划,搜索) 题面 Vjudge 你有一个数\(n\),\(n\le 10^{24}\),为了方便会告诉你\(n\)分解之后有\(m\) ...

  10. 123: The filename, directory name, or volume label syntax is incorrect今天玩nginx的时候报错

    今天在win下玩nginx的时候 提示500错误 看了下nginx的logs  提示 123: The filename, directory name, or volume label syntax ...