UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

2024-02-15 15:48

本文主要是介绍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]你的名字 (后缀自动机、线段树合并)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/711819

相关文章

基于C#实现PDF文件合并工具

《基于C#实现PDF文件合并工具》这篇文章主要为大家详细介绍了如何基于C#实现一个简单的PDF文件合并工具,文中的示例代码简洁易懂,有需要的小伙伴可以跟随小编一起学习一下... 界面主要用于发票PDF文件的合并。经常出差要报销的很有用。代码using System;using System.Col

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

不删数据还能合并磁盘? 让电脑C盘D盘合并并保留数据的技巧

《不删数据还能合并磁盘?让电脑C盘D盘合并并保留数据的技巧》在Windows操作系统中,合并C盘和D盘是一个相对复杂的任务,尤其是当你不希望删除其中的数据时,幸运的是,有几种方法可以实现这一目标且在... 在电脑生产时,制造商常为C盘分配较小的磁盘空间,以确保软件在运行过程中不会出现磁盘空间不足的问题。但在

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :