考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献。

T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长公共子串的长度。

从1到length(T)扫一遍,SAM基本操作求出每个前缀与S的最长公共子串。

答案为$\sum_{i=1}^{cnt}max(0,mx[x]-max(mx[fa[x]],len[tag[x]]))$,其中tag[x]是x所代表的子串在T中的第一个出现位置,len[i]为T的前缀i与S的最长公共子串。

再考虑l,r任意的情况,唯一区别在于求某前缀与S的最长公共子串时,需要满足[l,r]的限制。

即若想判断当前公共子串长是否可以为len,那么当前走到的点的parent树的子树内必须存在一个串的Right在[l+len,r]中。

“某子树内是否存在[l+len,r]中的值”显然可以用主席树做,或可持久化线段树合并。

下面大概证一下线段树合并的时空复杂度:

1.由于每次合并时,若发现x与y的当前子树有一个为空则退出。这种情况每个节点最多会出现一次(合并之后这个位置就不再为空了),故这部分复杂度为$O(n\log n)$。

2.除去情况一,则x与y合并的复杂度为两个树的重合节点个数。最坏情况发生在依次将一个个只有一个节点的树合并进来,这部分显然仍然是$O(n\log n)$的。

3.不可持久化的线段树合并并不产生新节点,故空间复杂度为初始插入n个点的复杂度:$O(n\log n)$

4.可持久化的线段树,根据算法流程容易发现时空复杂度同阶,于是复杂度也为$O(n\log n)$

 #include<cstdio>
#include<algorithm>
#include<cstring>
#define lson ls[x],L,mid
#define rson rs[x],mid+1,R
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=,M=;
char s[N],t[N];
int n,m,nd,Q,l,r,len[N],rt[N],id[N],ls[M],rs[M]; struct SAM{
int nd,lst,mx[N],fa[N],pos[N],son[N][]; void init(){
nd=; lst=;
rep(i,,m*) fa[i]=mx[i]=;
rep(i,,m*) rep(j,,) son[i][j]=;
} void ext(int c,int x){
int p=lst,np=lst=++nd; pos[np]=x; mx[np]=mx[p]+;
while (p && !son[p][c]) son[p][c]=np,p=fa[p];
if (!p) fa[np]=;
else{
int q=son[p][c];
if (mx[q]==mx[p]+) fa[np]=q;
else{
int nq=++nd; pos[nq]=pos[q]; mx[nq]=mx[p]+;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
}
}
} }S,T; bool cmp(int a,int b){ return S.mx[a]<S.mx[b]; } void ins(int &x,int L,int R,int k){
if (!x) x=++nd;
if (L==R) return;
int mid=(L+R)>>;
if (k<=mid) ins(lson,k); else ins(rson,k);
} bool que(int x,int L,int R,int l,int r){
if (!x) return ;
if (L==l && r==R) return ;
int mid=(L+R)>>;
if (r<=mid) return que(lson,l,r);
else if (l>mid) return que(rson,l,r);
else return que(lson,l,mid)|que(rson,mid+,r);
} int merge(int x,int y){
if (!x || !y) return x|y;
int now=++nd;
ls[now]=merge(ls[x],ls[y]);
rs[now]=merge(rs[x],rs[y]);
return now;
} int main(){
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
scanf("%s",s+); n=strlen(s+);
S.init(); rep(i,,n) S.ext(s[i]-'a',i),ins(rt[S.lst],,n,i);
rep(i,,S.nd) id[i]=i;
sort(id+,id+S.nd+,cmp);
for (int i=S.nd; i>; i--) rt[S.fa[id[i]]]=merge(rt[S.fa[id[i]]],rt[id[i]]);
for (scanf("%d",&Q); Q--; ){
scanf("%s%d%d",t+,&l,&r); m=strlen(t+);
int now=,x=;
T.init(); rep(i,,m) T.ext(t[i]-'a',i);
rep(i,,m){
int c=t[i]-'a';
while (){
if (!que(rt[S.son[x][c]],,n,l+now,r)){
if (!now) break; now--;
if (now==S.mx[S.fa[x]]) x=S.fa[x];
}else{ now++; x=S.son[x][c]; break; }
}
len[i]=now;
}
ll ans=;
rep(i,,T.nd) ans+=max(,T.mx[i]-max(T.mx[T.fa[i]],len[T.pos[i]]));
printf("%lld\n",ans);
}
return ;
}

[NOI2018]你的名字(SAM+线段树合并)的更多相关文章

  1. NOI2018 你的名字——SAM+线段树合并

    题目链接在这里洛谷/LOJ 题目大意 有一个串\(S\),每次询问给你一个串\(T\),两个数\(L\)和\(R\),问你\(T\)有多少个本质不同的子串不是\(S[L,R]\)的子串 SOLUTIO ...

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

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

  3. 【NOI2018】你的名字(SAM & 线段树合并)

    Description Hint Solution 不妨先讨论一下无区间限制的做法. 首先"子串"可以理解为"前缀的后缀",因此我们定义一个 \(\lim(i) ...

  4. UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...

  5. P4770-[NOI2018]你的名字【SAM,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/P4770 题目大意 给出一个长度为\(n\)的字符串\(S\).\(q\)次询问给出一个串\(T\)和一个区间\([ ...

  6. CF1037H Security——SAM+线段树合并

    又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...

  7. luogu4770 [NOI2018]你的名字 (SAM+主席树)

    对S建SAM,拿着T在上面跑 跑的时候不仅无法转移要跳parent,转移过去不在范围内也要跳parent(注意因为范围和长度有关,跳的时候应该把长度一点一点地缩) 这样就能得到对于T的每个前缀,它最长 ...

  8. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  9. Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...

随机推荐

  1. SpringCloud之Eureka(注册中心集群篇)

    一:集群环境搭建 第一步:我们新建两个注册中心工程一个叫eureka_register_service_master,另一个叫eureka_register_service_backup eureka ...

  2. go 切片

    切片定义 切片是基于数组类型做的一层封装.它非常灵活,可以自动扩容. var a []int //定义一个int类型的空切片 切片初始化, a[start:end]创建一个包括从start到end-1 ...

  3. centos 安装memcache服务后memcahce本机连接Permission

    自己手动在虚拟机下装了下memcache,整个过程真是充满波折,本身用php5.3安装memcache扩展就麻烦很多,无法通过yum直接安装,安装方法详见http://chenwei.me/blog/ ...

  4. Linux设备驱动程序学习 高级字符驱动程序操作[阻塞型I/O和非阻塞I/O]【转】

    转自:http://blog.csdn.net/jacobywu/article/details/7475432 阻塞型I/O和非阻塞I/O 阻塞:休眠 非阻塞:异步通知 一 休眠 安全地进入休眠的两 ...

  5. Ubuntu下 git 服务器的搭建【转】

    转自:http://www.open-open.com/lib/view/open1391477731082.html 搭建git服务器的4个步骤 1   配置服务器前的准备工作 首先ubuntu系统 ...

  6. ajax代码示例

    function loadXMLDoc(idName,url,sendOut) { var xmlhttp; if (window.XMLHttpRequest) {// code for IE7+, ...

  7. Android图片异步加载

    原:http://www.cnblogs.com/angeldevil/archive/2012/09/16/2687174.html 相关:https://github.com/nostra13/A ...

  8. OSGiBundle出现 Could not find bundle: org.eclipse.equinox.console的解决方案

    按照网上教程创建OSGI HelloWorld实例配置run configuration时出现Could not find bundle: org.eclipse.equinox.console 和C ...

  9. nfs挂载出错:mount.nfs: access denied by server while mounting

    这个问题就是服务器不允许客户端去挂载,那么修改服务端的权限 $ sudo vi /etc/hosts.deny 文本末添加 ### NFS DAEMONS portmap: ALL lockd: AL ...

  10. Scala中的"null" 和“_”来初始化对象

    Alternatives Use null as a last resort. As already mentioned, Option replaces most usages of null. I ...