P4770 [NOI2018] 你的名字 解题报告

先从 \(l=1,r=|s|\) 开始考虑

因为很难计算不重复的个数,考虑计算本质不同的与 \(s\) 的子串相同的个数,再用 \(t\) 中本质不同的子串数相减

因为涉及不同的子串数的计算以及匹配,所以可以对 \(s\)\(t\) 都建立 SAM

接下来,因为 SAM 的 link 指针和 AC 自动机的 fail 指针很像,可以直接把 \(t\) 放到 \(s\) 的 SAM 上匹配

此时,我们就可以得到以 \(x\) 为结尾,长度最少为多少时才不重叠,记为 \(lim\)

记每个节点 endpos 集合中最大的字符串长度为 \(len\)\(tag\) 表示该集合字符串第一次出现的结尾位置(因为集合里字符串互为后缀所以结尾相同)

统计答案时,parent 树上父亲的 \(len\) 一定不行,\(lim_tag\) 也不行,因为是后,会重,用 \(len_x\) 减去其中较大的就行了

接下来考虑加上 \(l\)\(r\) 的限制,此时,在匹配时就不是每条路都能走了

用线段树维护后缀自动机的 endpos 集合,如果一个节点的 endpos 包含点 x,就将这个点对应线段树的对应位置改成1

因为 parent 树上的 endpos 是包含关系,可以合并

此时,在转移时判断要走到的点有没有 endpos 在区间 \([l,r]\) 即可

代码:

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1000005,M=20000005;
int n,T,m;
string s,t;
struct SAM{int ch[26],link,len;
}s1[N];
int lst=1,tot=1,cnt[N],ids[N];
bool in[N];
void insert(int c)
{int p=lst,now=++tot;in[now]=1;s1[now].len=s1[lst].len+1;while(p&&!s1[p].ch[c]){s1[p].ch[c]=now;p=s1[p].link;}if(!p) s1[now].link=1;else{int q=s1[p].ch[c];if(s1[p].len+1==s1[q].len){s1[now].link=q;}else{int clone=++tot;s1[clone]=s1[q];s1[clone].len=s1[p].len+1;while(p&&s1[p].ch[c]==q){s1[p].ch[c]=clone;p=s1[p].link;}s1[q].link=s1[now].link=clone;}}lst=now; 
}
int rt[N],ls[M],rs[M],idx;
int update(int p,int l,int r,int x)
{if(!p) p=++idx;if(l==r){return p;}int mid=(l+r)>>1;if(x<=mid) ls[p]=update(ls[p],l,mid,x);else rs[p]=update(rs[p],mid+1,r,x);return p;
}
int merge(int a,int b)
{if(!a||!b) return a+b;int p=++idx;ls[p]=merge(ls[a],ls[b]);rs[p]=merge(rs[a],rs[b]);return p;
}
bool query(int p,int l,int r,int ql,int qr)
{if(!p||ql>qr) return 0;if(ql<=l&&qr>=r) return 1;int mid=(l+r)>>1;if(ql<=mid&&query(ls[p],l,mid,ql,qr)) return 1;if(qr>mid&&query(rs[p],mid+1,r,ql,qr)) return 1;return 0;
}
struct sam{int ch[26],link,len;void clear(){for(int i=0;i<26;i++) ch[i]=0;len=link=0;}
}s2[N*2];
int lst1,tot1,tag[N*2],lim[N*2];
void init()
{for(int i=0;i<=tot1;i++){s2[i].clear();}tot1=lst1=1;
}
void ins(int c)
{int p=lst1,now=++tot1;s2[now].len=tag[now]=s2[p].len+1;while(p&&!s2[p].ch[c]){s2[p].ch[c]=now;p=s2[p].link;}if(!p) s2[now].link=1;else{int q=s2[p].ch[c];if(s2[p].len+1==s2[q].len){s2[now].link=q;}else{int clone=++tot1;s2[clone]=s2[q];s2[clone].len=s2[p].len+1;tag[clone]=tag[q];while(p&&s2[p].ch[c]==q){s2[p].ch[c]=clone;p=s2[p].link;}s2[q].link=s2[now].link=clone;}}lst1=now;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>s;n=s.size();s=" "+s;for(int i=1;i<=n;i++) insert(s[i]-'a');
//	printf("%d ",tot);for(int i=1;i<=tot;i++){cnt[s1[i].len]++;
//		printf("%d ",s1[i].len);}for(int i=1;i<=tot;i++) cnt[i]+=cnt[i-1];for(int i=1;i<=tot;i++) ids[cnt[s1[i].len]--]=i;for(int i=tot;i>0;i--){int x=ids[i];
//		cout<<in[x]<<" "<<x<<'\n';if(in[x]) rt[x]=update(rt[x],1,n,s1[x].len);rt[s1[x].link]=merge(rt[s1[x].link],rt[x]);}cin>>T;while(T--){int l,r;cin>>t>>l>>r;m=t.size();t=" "+t;init();int p=1,len=0;for(int i=1;i<=m;i++){int c=t[i]-'a';ins(c);while(1){if(s1[p].ch[c]&&query(rt[s1[p].ch[c]],1,n,l+len,r)){len++,p=s1[p].ch[c];break;}if(len==0) break;len--;if(len==s1[s1[p].link].len)p=s1[p].link;}lim[i]=len;}ll ans=0;for(int i=2;i<=tot1;i++){ans=ans+max(0,s2[i].len-max(s2[s2[i].link].len,lim[tag[i]]));}cout<<ans<<'\n';}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/879237.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025省选模拟9

不知道啥2025省选模拟9 网络流专场是吧😅 Delov 的 npy 们 原题链接 一眼网络流,然后不会了。 发现正常顺着限制做做不了,考虑将限制转化一下,以 L 操作为例。 在 \(a_i\) 左侧的点中不超过 \(b_i\) 个,等价于从左往右数第 \(b_i+1\) 个点在 \(a_i\) 右侧。 但还是不好做…

海外社交媒体运营卡成狗?云手机一键“救场”

海外社交媒体运营卡成狗?云手机一键“救场” 在海外社交媒体运营中,运营人员往往会遇到各种挑战,导致运营效率低下,甚至感觉“卡成狗”。而云手机作为一种流行的成熟普及技术工具,可以为海外社交媒体运营带来一定的帮助,实现一键“救场”。以下是对云手机在海外社交媒体运…

MyBatis resultmap结果映射

创建数据库和实体类 首先创建数据库student和teacher,并且每个学生对应一个老师,一个老师可以对应多个学生,数据库如下图: 创建实体类Student1 package com.loubin.pojo;2 3 public class Student {4 private int id;5 private String name;6 7 Teacher teach…

Linguistics-English-Textbooks: 上海外教社: 高校英语专业系列教材(修订版)

新世纪高校英语专业系列教材(修订版) https://we.sflep.com/books/newcenturymajor1.aspx教材特色 配套资源 特点 秉以新《国标》指导下的英语专业课程改革为导向精心架构,体系完备。 凝聚海内外英语专业教育界专家学者智慧,教材编写高屋建瓴、深入 浅出. 兼顾语言基本技能…

OCRmyPDF: 让图片 PDF 可复制、搜索的神器

翻开十年前的工作报告,面对泛黄的纸质合同,整理成摞的文献资料 - 这些场景总离不开扫描仪,将纸张材料转成 PDF 扫描件电子版。 但生成的 PDF 文件像一张张定格照片,既不能复制文字,也无法搜索关键词。 图片 今天推荐的开源项目:OCRmyPDF 专治各种"哑巴PDF"。它…

10. 正则表达式

一、什么是正则表达式正则表达式(regular expression)又称 规则表达式,是一种文本模式(pattern)。正则表达式使用一个字符串来描述、匹配具有相同规格的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。正则表达式的核心功能就是处理文本。正则表达式并不仅…

独立开发经验谈:我是如何借助 Docker 环境变量让客户 1 分钟上线客服系统的

通过 Docker 环境变量,在启动容器时带入配置信息,自动写入配置文件中,完全免去了进入容器内部 vim 修改的步骤,真正实现在线客服系统1分钟上线。我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统。陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上…

线段树详解

授人以鱼不如授人以渔本文尽量详细地讲述线段树的引入,实现,应用,以及相关进阶知识。 引入 引入线段树通用的例子: 给定一组整数\(nums\),定义两种操作修改列表里的第\(i\)个数据为\(val\) ①查询区间和\([L,R]\) ②为了同时实现两种操作,现在考虑处理\(nums\)的方式 简单…

[Python] 依赖注入的使用,多模块任务隔离

使用google/pinject(依赖注入库)搭建了一个多模块运行、相互隔绝的项目。定义全局单例的依赖注入容器:"""依赖注入容器"""from typing import Any, List, Type, TypeVar import pinject import pinject.findingclass Ioc:"""依…

关于设计模式的一点想法

《设计模式:可复用面向对象软件的基础》书评最早读这本《设计模式:可复用面向对象软件的基础》是在大学的时候。读了一些片段,看到了讲文本编辑器的滚动条装饰,觉得有点意思,可以用来做图形界面。记得有一天晚上上床睡觉后,和两位同寝室室友聊天。一位室友LL说,他为了找…

ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明

容器存储是容器应用运行时的数据保障,本次 ACK 容器存储监控的更新能够帮助用户全面、精细地掌控集群中的存储细节,快速定位业务运行过程中可能出现的 IO 瓶颈和 IO 问题,更好地保证业务的平稳运行。作者:邱圆辉(霜序) 背景 随着容器化应用的日益普及、业务规模的增长以及…