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]你的名字 (后缀自动机、线段树合并)的更多相关文章

  1. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  2. BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)

    LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...

  3. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  4. luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并

    其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...

  5. [NOI2018]你的名字(后缀自动机+线段树)

    题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...

  6. 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]

    传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...

  7. [NOI2018]你的名字(后缀自动机+线段树合并)

    看到题目名字去补番是种怎么样的体验 我只会 \(68\) 分,打了个暴力.正解看了一会儿,发现跟 \([HEOI2016/TJOI2016]\) 字符串很像,用线段树合并维护 \(endpos\) 集 ...

  8. BZOJ 3413 匹配 (后缀自动机+线段树合并)

    题目大意: 懒得概括了 神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题 线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个 ...

  9. Luogu4770 NOI2018你的名字(后缀自动机+线段树合并)

    先考虑l=1,r=n,并且不要求本质不同的情况.对原串建SAM,将询问串在上面跑,得到每个前缀的最长匹配后缀即可得到答案. 然后考虑本质不同.对询问串也建SAM,统计每个节点的贡献,得到该点right ...

随机推荐

  1. Objective-C中的自动释放池

    自动释放池块@autoreleasepool 自动释放池块在MRC和ARC下都可以使用.在MARC下,为了将自动释放池块内部的变量放入自动释放池,需要手动调用autorelease方法:在ARC下,只 ...

  2. springBoot2.0使用@slf4j注解记录日志

    1. idea上安装Lombok插件 File --> setting --> Plugins 安装完后重启idea 2. 在springboot项目中修改pom.xml,添加如下配置引入 ...

  3. centos7搭建单机redis5.0

    目录 1. redis初步安装 2. 配置 3. 设置开机启动(centos6) 3.1 编写启动脚本 3.2 设置权限 3.3 启动测试 3.4 设置开机自启动 4. 设置开机启动(centos7) ...

  4. Python 集合的各种操作 数学运算 关系操作 增删改查 生成式

    # 集合是无序的 是可变的 不能重复 允许数学运算 分散存储 # 创建# collegel = {'哲学','经济学','法学','教育学'}## # {'教育学', '经济学', '法学', '哲学 ...

  5. C++中如何实现split的效果?

    C++中如何实现split的效果? 和Python等语言不同,C++的string类没有内置split函数,这对于实际应用中要经常分割字符串的情况非常不方便.有很多种方法来处理,这里讲一种比较方(to ...

  6. 090、ELK完成部署和使用 (2019-05-13 周二)

    参考https://www.cnblogs.com/CloudMan6/p/7787870.html   上节我们已经部署了容器化的ELK,本节我们学习如何将日志导入ELK并进行图形化展示.   几乎 ...

  7. 利用bing图片搜索接口开发图片搜索应用程序

    概述:通过bing的图片搜索引擎,开发自己的图片搜索应用程序.bing的图片搜索接口是收费的,但是初次注册使用,key可以免费试用30天 程序运行效果如下 一,代码如下 static SearchRe ...

  8. mysql数据库基础命令(一)

    用户与权限 创建用户 mysql>create user test identified by 'BaC321@#'; 修改密码 ##5.5版本及以前的命令 mysql>set passw ...

  9. [Linux]Linux下经常会用到的简单实例(持续更新)

    1.查找某些进程并结束他们: ps -elf | grep '进程关键字' | awk '{print $4}'| xargs kill -9 解析: ps -elf 的 -e 代表列出所有进程,-l ...

  10. Java RMI 最简单实例

    IHello.java import java.rmi.Remote; import java.rmi.RemoteException; public interface IHello extends ...