题目描述

小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. WCF(二)配置文件

    上篇文章中对WCF的配置放到App.config中,这样可以使程序更灵活.更具有扩展性. 下面说下配置文件中各个节点的含义. 服务端: WCF配置文件节点放在<system.serviceMod ...

  2. Win10 八步打通 Nuget 发布打包

    我们可以使用Nuget 下载你所需要的资源包还可以将自己封装好的各种控件包 工具包 等上传nuget 我们只需要几步就完成你要发布的包. 第一步:编译你的控件 anycpu debug/release ...

  3. ZBrush中标准几何体与Polymesh

    通过对ZBrush的学习,相信您已经对这款软件有了一定的了解,文本我们主要学习ZBrush®的3D物体标准几何体的特性和使用方法.在ZBrush中只有Polymesh(多边形网格)物体才能使用雕刻笔刷 ...

  4. 路飞学城Python-Day46

    16-如何正确的使用类选择器及总结 一般情况下尽量不要去使用id选择器,因为id选择器有很大的限制性,id一般都是JS配合使用的,类选择器都是和CSS配合使用的,特殊性情况可以用id选择器. 类的使用 ...

  5. baidu练习/html/css

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

  6. PHP内置SOAP扩展客户端的使用例子

    SOAP已经是属于OUT范畴的技术了,不过因为历史原因,时不时还是会用到它,以前都是用NuSOAP,现在准备试试PHP内置的SOAP扩展.由于文本只打算说说客户端的用法,所以得先找一些能直接用的服务端 ...

  7. laravel 授权、用户验证

    记录帖 一.授权 只允许管理员删除用户,给管理员授权时,可以这样做,首先: 创建UserPolicy类: php artisan make:policy UserPolicy  然后在UserPoli ...

  8. [读书笔记] Python数据分析 (一) 准备工作

    1. python中数据结构:矩阵,数组,数据框,通过关键列相互联系的多个表(SQL主键,外键),时间序列 2. python 解释型语言,程序员时间和CPU时间衡量,高频交易系统 3. 全局解释器锁 ...

  9. 【安装配置Redis】

    目录 安装 配置 Redis官网:https://redis.io Redis是完全开源免费的,遵守BSD协议. Redis是一个高性能的key-value数据库. @ *** Redis具有以下特点 ...

  10. MD5加密技术

    前几天,在看OpenVXI3.4的时候,偶然发现了几个奇怪的文件,那就是OpenVXI-3.4\src\cache下面的,base64.c,base64.h,md5.c,md5.h.既然有人把源代码给 ...