UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)
NOI2019考前做NOI2018题。。
题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417
(luogu) https://www.luogu.org/problemnew/show/P4770
(uoj) http://uoj.ac/problem/395
题解: 其实非常水,转化成\(S[l,r]\)和\(T\)有多少本质不同的公共子串,我们就是要求出串\(T\)每个位置与\(S[l,r]\)最长匹配的长度。那么就对\(S\)建SAM,把\(T\)在\(S\)上跑,每次到达一个点之后如果它的子树内没有\([l,r]\)之间的点就跳fa. 求子树内的点(即Right集合)可以自上而下线段树合并。因为要求本质不同的公共子串个数,那么对\(T\)也建立后缀自动机,统计T的后缀自动机上每个点与S的公共子串个数。细节挺多,挂了好几次,详见代码。
时间复杂度\(O(n\log n)\), \(n\)为输入字符串总长
字符串细节真的好多啊,提醒自己明天如果写字符串题一定要对拍!(快算了吧,我必不可能会做字符串)
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
#define llong long long
using namespace std;
inline int read()
{
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
if(f) return x;
return -x;
}
const int N = 5e5;
const int S = 26;
const int LGN = 21;
struct SuffixAutomaton
{
int fa[(N<<1)+3];
int len[(N<<1)+3];
int son[(N<<1)+3][S+1];
int ord[(N<<1)+3];
int buc[(N<<1)+3];
int rtn,siz,lstpos;
void init()
{
for(int i=1; i<=siz; i++)
{
len[i] = fa[i] = 0;
for(int j=1; j<=S; j++) son[i][j] = 0;
}
rtn = siz = lstpos = 1;
}
int insertchar(int ch)
{
int p = lstpos,np; siz++; np = lstpos = siz; len[np] = len[p]+1;
for(; p && son[p][ch]==0; p=fa[p]) {son[p][ch] = np;}
if(p==0) {fa[np] = 1;}
else
{
int q = son[p][ch];
if(len[q]==len[p]+1) {fa[np] = q;}
else
{
siz++; int nq = siz; len[nq] = len[p]+1;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq] = fa[q]; fa[np] = fa[q] = nq;
for(; p && son[p][ch]==q; p=fa[p]) {son[p][ch] = nq;}
}
}
return np;
}
void getord()
{
for(int i=1; i<=siz; i++) buc[i] = 0;
for(int i=1; i<=siz; i++) buc[len[i]]++;
for(int i=1; i<=siz; i++) buc[i] += buc[i-1];
for(int i=siz; i>=1; i--) ord[buc[len[i]]--] = i;
}
} sam,samt;
struct SegmentTree
{
int ls,rs;
} sgt[(N<<1)*LGN+3];
char a[N+3],b[N+3];
int mxl[(N<<1)+3];
int rt[(N<<1)+3];
int n,m,q,rtn,siz;
void addval(int &pos,int le,int ri,int lrb)
{
siz++; sgt[siz] = sgt[pos]; pos = siz;
if(le==lrb && ri==lrb) {return;}
int mid = (le+ri)>>1;
if(lrb<=mid) {addval(sgt[pos].ls,le,mid,lrb);}
else {addval(sgt[pos].rs,mid+1,ri,lrb);}
}
int merge(int pos1,int pos2)
{
if(pos1==0) return pos2; if(pos2==0) return pos1;
siz++; int pos = siz;
sgt[pos].ls = merge(sgt[pos1].ls,sgt[pos2].ls);
sgt[pos].rs = merge(sgt[pos1].rs,sgt[pos2].rs);
return pos;
}
int query(int pos,int le,int ri,int rb) //largest <=rb
{
if(pos==0) return 0;
if(le==ri) return le;
int mid = (le+ri)>>1;
if(rb<=mid) return query(sgt[pos].ls,le,mid,rb);
else
{
int tmp = query(sgt[pos].rs,mid+1,ri,rb);
return tmp ? tmp : query(sgt[pos].ls,le,mid,rb);
}
}
int main()
{
scanf("%s",a+1); n = strlen(a+1);
for(int i=1; i<=n; i++) a[i] -= 96;
sam.init();
rtn = siz = 1;
for(int i=1; i<=n; i++)
{
int u = sam.insertchar(a[i]);
rt[u] = rtn; addval(rt[u],1,n,i);
}
sam.getord();
for(int i=sam.siz; i>=1; i--)
{
int u = sam.ord[i];
rt[sam.fa[u]] = merge(rt[sam.fa[u]],rt[u]);
}
scanf("%d",&q);
while(q--)
{
for(int i=1; i<=samt.siz; i++) mxl[i] = 0;
samt.init();
scanf("%s",b+1); m = strlen(b+1); int lb,rb; scanf("%d%d",&lb,&rb);
for(int i=1; i<=m; i++) b[i]-=96;
for(int i=1; i<=m; i++) samt.insertchar(b[i]);
samt.getord();
int u = sam.rtn,uu = samt.rtn,cur = 0; llong ans = 0ll;
for(int i=1; i<=m; i++)
{
while(u && sam.son[u][b[i]]==0) {u = sam.fa[u]; cur = sam.len[u];}
if(u!=0) {u = sam.son[u][b[i]]; cur++;} else {u = sam.rtn; cur = 0;}
int tmp = query(rt[u],1,n,rb);
while(u && tmp<lb+sam.len[sam.fa[u]]) {u = sam.fa[u]; tmp = query(rt[u],1,n,rb); cur = min(sam.len[u],tmp-lb+1);}
cur = min(cur,tmp-lb+1); //注意此处必须受到区间限制
while(uu && samt.son[uu][b[i]]==0) {uu = samt.fa[uu];}
uu = uu==0 ? 1 : samt.son[uu][b[i]];
mxl[uu] = max(mxl[uu],cur);
}
for(int i=samt.siz; i>=1; i--)
{
int uu = samt.ord[i];
mxl[samt.fa[uu]] = max(mxl[samt.fa[uu]],mxl[uu]);
}
for(int i=1; i<=samt.siz; i++)
{
mxl[i] = max(min(mxl[i],samt.len[i]),samt.len[samt.fa[i]]); //最长匹配的长度,不得小于该点最短长度
int tmp = samt.len[i]-mxl[i]; //该节点上不匹配的个数
ans += (llong)tmp;
}
printf("%lld\n",ans);
}
return 0;
}
UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)的更多相关文章
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)
LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
- luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并
其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...
- [NOI2018]你的名字(后缀自动机+线段树)
题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...
- 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...
- [NOI2018]你的名字(后缀自动机+线段树合并)
看到题目名字去补番是种怎么样的体验 我只会 \(68\) 分,打了个暴力.正解看了一会儿,发现跟 \([HEOI2016/TJOI2016]\) 字符串很像,用线段树合并维护 \(endpos\) 集 ...
- BZOJ 3413 匹配 (后缀自动机+线段树合并)
题目大意: 懒得概括了 神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题 线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个 ...
- Luogu4770 NOI2018你的名字(后缀自动机+线段树合并)
先考虑l=1,r=n,并且不要求本质不同的情况.对原串建SAM,将询问串在上面跑,得到每个前缀的最长匹配后缀即可得到答案. 然后考虑本质不同.对询问串也建SAM,统计每个节点的贡献,得到该点right ...
随机推荐
- Objective-C中的自动释放池
自动释放池块@autoreleasepool 自动释放池块在MRC和ARC下都可以使用.在MARC下,为了将自动释放池块内部的变量放入自动释放池,需要手动调用autorelease方法:在ARC下,只 ...
- springBoot2.0使用@slf4j注解记录日志
1. idea上安装Lombok插件 File --> setting --> Plugins 安装完后重启idea 2. 在springboot项目中修改pom.xml,添加如下配置引入 ...
- centos7搭建单机redis5.0
目录 1. redis初步安装 2. 配置 3. 设置开机启动(centos6) 3.1 编写启动脚本 3.2 设置权限 3.3 启动测试 3.4 设置开机自启动 4. 设置开机启动(centos7) ...
- Python 集合的各种操作 数学运算 关系操作 增删改查 生成式
# 集合是无序的 是可变的 不能重复 允许数学运算 分散存储 # 创建# collegel = {'哲学','经济学','法学','教育学'}## # {'教育学', '经济学', '法学', '哲学 ...
- C++中如何实现split的效果?
C++中如何实现split的效果? 和Python等语言不同,C++的string类没有内置split函数,这对于实际应用中要经常分割字符串的情况非常不方便.有很多种方法来处理,这里讲一种比较方(to ...
- 090、ELK完成部署和使用 (2019-05-13 周二)
参考https://www.cnblogs.com/CloudMan6/p/7787870.html 上节我们已经部署了容器化的ELK,本节我们学习如何将日志导入ELK并进行图形化展示. 几乎 ...
- 利用bing图片搜索接口开发图片搜索应用程序
概述:通过bing的图片搜索引擎,开发自己的图片搜索应用程序.bing的图片搜索接口是收费的,但是初次注册使用,key可以免费试用30天 程序运行效果如下 一,代码如下 static SearchRe ...
- mysql数据库基础命令(一)
用户与权限 创建用户 mysql>create user test identified by 'BaC321@#'; 修改密码 ##5.5版本及以前的命令 mysql>set passw ...
- [Linux]Linux下经常会用到的简单实例(持续更新)
1.查找某些进程并结束他们: ps -elf | grep '进程关键字' | awk '{print $4}'| xargs kill -9 解析: ps -elf 的 -e 代表列出所有进程,-l ...
- Java RMI 最简单实例
IHello.java import java.rmi.Remote; import java.rmi.RemoteException; public interface IHello extends ...