题目描述

小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。

由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同。

由于一些特殊的原因,小A 不知道ION2017 每道题的名字,但是他通过一些特殊手段得到了ION2017 的命名串,现在小A 有Q 次询问:每次给定ION2017 的命名串和ION2018 的命名串,求有几种题目的命名,使得这个名字一定满足命题委员会的规定,即是ION2018 的命名串的一个非空连续子串且一定不会和ION2017 的任何一道题目的名字相同。

由于一些特殊原因,所有询问给出的ION2017 的命名串都是某个串的连续子串,详细可见输入格式。

题解:68 $pts$ 做法:
令 $mx[i]$ 表示 $T$ 中 $[1...i]$ 中与 $S$ 最长匹配长度且为 $[1...i]$ 中后缀
如果不考虑重复,则 $ans=\sum i-mx[i]$
将同一个子串算作一个,我们对 $T$ 也构建后缀自动机
插入当前字符 $arr[i]$ 后自动机末尾节点为 $np$,那么对于 $mx[i]$ 有两种情况
#1. $mx[i]\in(len[f_{np}], len[np]]$ 那么这一部分就由 $np$ 贡献
#2. $mx[i]\in[1,len[f_{np}]]$ 那么这一部分就由 $f_{np}$ 贡献
一定要注意,这里是一边插入字符一边建立 $T$ 的 $SAM$ 的,所以如果对 $np$贡献
那么后缀树中 $np$ 祖先中所有节点在建出时都是可以与 $S$ 进行完全匹配的 !!
所以他的祖先都是一点都不贡献的 !!!!!
只会贡献那个极长的后缀,所以这个做法就是对的
最后,答案即为 $\sum_{i=1}^{tot}max(0,len[i]-max(mx[pos_{i}],len[f_{i}])$

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 3000008
#define ll long long
#define N 30
using namespace std;
int pos[maxn],n,queries,mx[maxn];
char str[maxn],ss[maxn];
namespace SAM1{
int last,tot,dis[maxn],ch[maxn][N],f[maxn];
void init() { last=tot=1; }
void ins(int c){
int p=last,np=++tot; last=np; dis[np]=dis[p]+1;
while(p&&!ch[p][c])ch[p][c]=np,p=f[p];
if(!p) f[np]=1;
else{
int q=ch[p][c],nq;
if(dis[q]==dis[p]+1) f[np]=q;
else{
nq=++tot;
dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
f[nq]=f[q],f[q]=f[np]=nq;
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
}
}
}
void solve(){
int nn=strlen(ss),p=1,cnt=0;
for(int i=0;i<nn;++i) {
while(p&&!ch[p][ss[i]-'a']) p=f[p],cnt=dis[p];
if(!p) {p=1;cnt=0; }
else ++cnt,p=ch[p][ss[i]-'a'];
mx[i+1]=cnt;
}
}
};
namespace SAM2{
int last,tot,dis[maxn],ch[maxn][N],f[maxn];
void init(){last=++tot; }
void ins(int c,int y,int rt){
int p=last,np=++tot; last=np; dis[np]=dis[p]+1; pos[np]=y;
while(p&&!ch[p][c])ch[p][c]=np,p=f[p];
if(!p) f[np]=rt;
else{
int q=ch[p][c],nq;
if(dis[q]==dis[p]+1) f[np]=q;
else{
nq=++tot;
dis[nq]=dis[p]+1;
pos[nq]=pos[q];
memcpy(ch[nq],ch[q],sizeof(ch[q]));
f[nq]=f[q],f[q]=f[np]=nq;
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
}
}
}
ll solve(int l){
ll sum=0;
for(int i=l+1;i<=tot;++i)
sum+=max(0,dis[i]-max(dis[f[i]],mx[pos[i]]));
return sum;
}
};
int main(){
//setIO("input");
scanf("%s",str),n=strlen(str),SAM1::init();
for(int i=0;i<n;++i) SAM1::ins(str[i]-'a');
scanf("%d",&queries);
while(queries--){
SAM2::init();
int rt=SAM2::tot,l,r,nn;
scanf("%s%d%d",ss,&l,&r),nn=strlen(ss);
for(int i=0;i<nn;++i) SAM2::ins(ss[i]-'a',i+1,rt);
SAM1::solve();
printf("%lld\n",SAM2::solve(rt));
}
return 0;
}

  

[NOI2018]你的名字(68pts) 后缀自动机的更多相关文章

  1. 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)

    传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...

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

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

  3. Luogu4770 NOI2018你的名字(后缀自动机+线段树合并)

    先考虑l=1,r=n,并且不要求本质不同的情况.对原串建SAM,将询问串在上面跑,得到每个前缀的最长匹配后缀即可得到答案. 然后考虑本质不同.对询问串也建SAM,统计每个节点的贡献,得到该点right ...

  4. 【洛谷4770/UOJ395】[NOI2018]你的名字(后缀数组_线段树合并)

    题目: 洛谷4770 UOJ395 分析: 一个很好的SAM应用题-- 一句话题意:给定一个字符串\(S\).每次询问给定字符串\(T\)和两个整数\(l\).\(r\),求\(T\)有多少个本质不同 ...

  5. Luogu4770 NOI2018你的名字(后缀数组+线段树)

    即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...

  6. 解题:NOI2018 你的名字(68pts暴力)

    题面 rt,如果省选没退役就补 SAM的优势:简单明了 先建S的SAM并标记所有节点,之后每次询问直接把T按广义SAM的方法插上去,统计新加的节点到根的状态代表的本质不同子串数,减掉被标记的部分就是T ...

  7. [NOI2018]你的名字(68pts)

    SAM真让人头秃. 题面 https://www.luogu.org/problemnew/show/P4770 首先考虑 l=1,r=∣S∣的做法 如果对于ION2018的子串不用判重的话,对ION ...

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

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

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

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

随机推荐

  1. vue中slot的用法案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. C# 基础复习 四 多线程

    单线程和多线程的区别     单线程:         只用主线程处理,如果一个操作在占用主线程,那么其他操作则无法执行     多线程:         除了主线程外,还开启了子线程来执行操作,子线 ...

  3. git diff详解

    这篇文章很好很好 https://www.cnblogs.com/alfayed/p/4682780.html

  4. kafka内外网集群配置

    linux下配置使用以第一台为例(先配置好jdk环境)1.解压kafka:2.10-0.10.1.12.修改zookeeper.properties 新增配置:maxClientCnxns=0 tic ...

  5. 使用iframe标签时如何通过jquery隐藏滚动条

    通过mouseover和mouseout事件来控制iframe的滚动条 代码如下:

  6. poj 3311 Hie with the Pie (状压dp) (Tsp问题)

    这道题就是Tsp问题,稍微加了些改变 注意以下问题 (1)每个点可以经过多次,这里就可以用弗洛伊德初始化最短距离 (2)在循环中集合可以用S表示更清晰一些 (3)第一维为状态,第二维为在哪个点,不要写 ...

  7. Cygwin(类UNIX模拟环境)&CURL(强大的http命令行工具)

    前言: 需要我用curl试下能否发送post请求调起公司的仿真系统(目前) 跟着大佬的脚步,亲测一把~ 感谢大佬的提供的博客和指导 @咩神 个人博客园及来源地址 Cygwin(类UNIX模拟环境) 一 ...

  8. 关于android的设备管理器-DevicePolicyManager(一)

    在Andorid的设置->安全里面有个设备管理器的选项,相信大部分android用户都不太会去注意这个东西.近期在安装了一个应用之后发现这个里面的东西变了.怎么回事呢,研究研究看看.</s ...

  9. server.htaccess 具体解释以及 .htaccess 參数说明

    .htaccess文件(或者"分布式配置文件")提供了针对文件夹改变配置的方法. 即.在一个特定的文档文件夹中放置一个包括一个或多个指令的文件, 以作用于此文件夹及其所有子文件夹. ...

  10. 一个build.xml实例

    <?xml version="1.0"?> <project name="ssh" basedir="." default ...