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. Mybatis(一) 入门

    对原生态jdbc程序中问题总结 创建mysql数据库 jdbc程序 使用jdbc查询mysql数据库中用户表的记录. 创建java工程,加入jar包 数据库驱动包 第一个是mysql驱动 第二个是or ...

  2. [ZJOI2007]时态同步 题解

    题面 这道题是一道比较水的XXOI题: 我们可以发现,反着思考题目就变为了让所有叶子节点同时发出信号,然后这些信号同时到达根节点: 可以证明,这样答案不会改变: 那么我们可以自下而上dfs(),设f[ ...

  3. 树型DP入门

    题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上司,现在已知每个人的活跃指数和上司关系(当然不可能存在环),求邀请哪些人(多少人)来能使得晚 ...

  4. C++中的多重继承(一)

    1,C++ 中是否允许一个类继承自多个父类? 1,可以: 2,这种情况就是多重继承: 3,多重继承的表象就是一个类有多个父类: 4,这是 C++ 非常特别的一个特性,在其他的程序设计语言中比如 C#. ...

  5. Maekdown光速习得

    菜鸟教程提供的在线编辑器,花了十分钟就学会了,可以完成简单编辑,详细学习可点击CSDN左上角Markdown在线编辑器. 菜鸟教程在线编辑器:传送门 CSDN在线编辑器:传送门

  6. py3 base64加密

    import base64 #字符串编码: encodestr = base64.b64encode('abcr34r344r'.encode('utf-8')) print(str(encodest ...

  7. 【golang】浅析rune数据类型

    golang中string底层是通过byte数组实现的.中文字符在unicode下占2个字节,在utf-8编码下占3个字节,而golang默认编码正好是utf-8. golang中还有一个byte数据 ...

  8. docker安装教程-centos

    Docker 要求CentOS 系统的内核版本在3.10以上,查看本页面的前提条件来验证你的CentOS 版本是否支持Docker . 1.通过uname -r 命令查看你当前的内核版本 uname ...

  9. 在easyui中解决使west和center为1:1,并且拖动窗口时能够自适应变化

    <script type="text/javascript"> // 解决页面两个grid的布局问题 $(function(){// 在页面加载完毕后 //consol ...

  10. 关于strcpy的安全函数的选择

    1)如果整个程序,在进行字符拷贝时,这个拷贝字符串的完整性,不影响整个程 序的运行,可以让其截取一部分字符串,程序继续执行.那么我们就可以选择安全 函数:strncpy_s 2)如果在进行字符串拷贝时 ...