考虑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. spring-boot-CommandLineRunner

    在项目服务启动完成后就去加载一些数据 @Component public class MyStartupRunner1 implements CommandLineRunner { @Override ...

  2. 使用mysql的SUBSTRING_INDEX函数解决项目中编码非重复问题的实现方案!

    一 SUBSTRING_INDEX函数介绍 作用:按关键字截取字符串 substring_index(str,delim,count) 说明:substring_index(被截取字段,关键字,关键字 ...

  3. imperva 非交互式导入导出配置

    非交互使用模式full_expimp.sh可以导出/导入手动使用交互式CLI 在root的命令行下执行: 例子:导出:# full_expimp.sh --operation=1 --pwd=密码 - ...

  4. Python Webdriver 重新使用已经打开的浏览器实例

    因为Webdriver每次实例化都会新开一个全新的浏览器会话,在有些情况下需要复用之前打开未关闭的会话.比如爬虫,希望结束脚本时,让浏览器处于空闲状态.当脚本重新运行时,它将继续使用这个会话工作.还就 ...

  5. awk的常用内置函数的使用【转】

    手把手教你在linux下熟悉使用awk的指令结构 (15) 大家好,今天和大家说一下awk吧.反正正则 早晚也要和大家说,不如一点一点和大家先交代清楚了,省得以后和大家说的时候,大家有懵的感觉... ...

  6. openjudge-NOI 2.5-1756 八皇后

    题目链接:http://noi.openjudge.cn/ch0205/1756/ 题解: 上一道题稍作改动…… #include<cstdio> #include<algorith ...

  7. 十五、springboot集成定时任务(Scheduling Tasks)(二)之(线程配置)

    配置类: /** * 定时任务线程配置 * */ @Configuration public class SchedulerConfig implements SchedulingConfigurer ...

  8. 16 Go Concurrency Patterns: Timing out, moving on GO并发模式: 超时, 继续前进

    Go Concurrency Patterns: Timing out, moving on  GO并发模式: 超时, 继续前进 23 September 2010 Concurrent progra ...

  9. MySQL字符集编码相关

    Windows 10家庭中文版,MySQL  5.7.20,2018-05-07 Part.1 查找数据库的字符集编码 查看MySQL字符集编码:status命令 使用命令行登录MySQL服务器,然后 ...

  10. java基础72 junit单元测试

    1.junit要注意的细节 1.如果junit测试一个方法,在junit窗口上显示绿色代表测试成功:如果显示红条,则代表测试方法出现异常不通过.    2.如果点击方法名.包名.类名.工程名运行jun ...