后缀数组学习笔记

后缀数组学习笔记


说在前边

  1. 学习了《后缀数组——处理字符串的有力工具》终于感觉入门了,就总结一下,主要是应用
  2. 原理讲解学习了 大佬Blog

一些性质

height数组:定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀。那么对于j和k,不妨设rank[j]<rank[k],则有以下性质:
suffix(j)和suffix(k)的最长公共前缀为height[rank[j]+1],height[rank[j]+2],height[rank[j]+3],……,height[rank[k]]中的最小值。

例1: 最长公共前缀

做法:height[rank[j]+1],height[rank[j]+2],height[rank[j]+3],……,height[rank[k]]中的最小值,RMQ解决

#include <bits/stdc++.h>
const int N = 100005;
using namespace std;
char ch[N],str[N];
int SA[N],rnk[N],Height[N],tax[N],tp[N],a[N],n,m;
void RSort() {for(int i=0;i<=m;++i) tax[i]=0;for(int i=1;i<=n;++i) ++tax[rnk[tp[i]]];for(int i=1;i<=m;++i) tax[i]+=tax[i-1];for(int i=n;i>=1;--i) SA[tax[rnk[tp[i]]]--]=tp[i];
}
int cmp(int *f,int x,int y,int w) {return f[x]==f[y]&&f[x+w]==f[y+w];
}
void Suffix() {for(int i=1;i<=n;++i) rnk[i]=a[i],tp[i]=i;m = 127, RSort();for(int w=1,p=1,i;p<n;w+=w,m=p) {for(p=0,i=n-w+1;i<=n;++i) tp[++p]=i;for(i=1;i<=n;++i) if(SA[i]>w) tp[++p] = SA[i]-w;RSort(), swap(rnk,tp),rnk[SA[1]]=p=1;for(i=2;i<=n;++i) rnk[SA[i]]=cmp(tp,SA[i],SA[i-1],w)?p:++p;}int j,k=0;for(int i=1;i<=n;Height[rnk[i++]]=k)for(k=k?k-1:k,j=SA[rnk[i]-1];a[i+k]==a[j+k];++k);
}
void init() {scanf(" %s",str);n = strlen(str);for(int i=0;i<n;++i) a[i+1] = str[i];
}
int Log[N],rmq[N][30];
void init_rmq() {Log[1] = 0;for(int i=2;i<=n;++i) Log[i] = Log[i>>1] + 1;for(int i=1;i<=n;++i) rmq[i][0] = Height[i];for(int j=1;j<=20;++j)for(int i=1;i+(1<<(j-1))<=n;++i)rmq[i][j] = min(rmq[i][j-1],rmq[i+(1<<j-1)][j-1]);
}
int RMQ_mn(int l,int r){int L=Log[r-l+1];return min(rmq[l][L],rmq[r-(1<<L)+1][L]);
}
int ask(int x,int y) {x=rnk[x],y=rnk[y];if(x>y)swap(x,y);return RMQ_mn(x+1,y);
}
int main() {init();Suffix();init_rmq();int q;scanf("%d",&q);while(q--) {int x,y;scanf("%d%d",&x,&y);printf("%d\n",ask(x,y));}return 0;
}

例2:可重叠最长重复子串

题意:给定一个字符串,求最长重复子串,这两个子串可以重叠。
做法:求height数组里的最大值。首先求最长重复子串,等价于求两个后缀的最长公共前缀的最大值。因为任意两个后缀的最长公共前缀都是height数组里某一段的最小值,那么这个值一定不大于height数组里的最大值。所以最长重复子串的长度就是height数组里的最大值。这个做法的时间复杂度为O(n)。

例3:不可重叠最长重复子串

题意:给定一个字符串,求最长重复子串,这两个子串不能重叠。
做法:先二分答案,把题目变成判定性问题:判断是否存在两个长度为k的子串是相同的,且不重叠。解决这个问题的关键还是利用height数组。把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。然后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于k。如果有一组满足,则说明存在,否则不存在。整个做法的时间复杂度为O(nlogn)。

例4:可重叠的 k次最长重复子串

题意:给定一个字符串,求至少出现k次的最长重复子串,这k个子串可以重叠。
做法:二分答案,然后将后缀分成若干组。不同的是,这里要判断的是有没有一个组的后缀个数不小于k。如果有,那么存在k个相同的子串满足条件,否则不存在。这个做法的时间复杂度为 O(nlogn)。

例5:不相同的子串的个数

题意:给定一个字符串,求不相同的子串的个数。
做法:每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。如果所有的后缀按照suffix(sa1),suffix(sa[2]),suffix(sa[3]),……,suffix(sa[n])的顺序计算,不难发现,对于每一次新加进来的后缀suffix(sa[k]),它将产生n-sa[k]+1个新的前缀。但是其中有height[k]个是和前面的字符串的前缀是相同的。所以suffix(sa[k])将“贡献”出n-sa[k]+1-height[k]个不同的子串。累加后便是原问题的答案。这个做法的时间复杂度为O(n)。

#include <bits/stdc++.h>
const int N = 100005;
using namespace std;
char ch[N],str[N];
int SA[N],rnk[N],Height[N],tax[N],tp[N],a[N],n,m;
void RSort() {for(int i=0;i<=m;++i) tax[i]=0;for(int i=1;i<=n;++i) ++tax[rnk[tp[i]]];for(int i=1;i<=m;++i) tax[i]+=tax[i-1];for(int i=n;i>=1;--i) SA[tax[rnk[tp[i]]]--]=tp[i];
}
int cmp(int *f,int x,int y,int w) {return f[x]==f[y]&&f[x+w]==f[y+w];
}
void Suffix() {for(int i=1;i<=n;++i) rnk[i]=a[i],tp[i]=i;m = 127, RSort();for(int w=1,p=1,i;p<n;w+=w,m=p) {for(p=0,i=n-w+1;i<=n;++i) tp[++p]=i;for(i=1;i<=n;++i) if(SA[i]>w) tp[++p] = SA[i]-w;RSort(), swap(rnk,tp),rnk[SA[1]]=p=1;for(i=2;i<=n;++i) rnk[SA[i]]=cmp(tp,SA[i],SA[i-1],w)?p:++p;}int j,k=0;for(int i=1;i<=n;Height[rnk[i++]]=k)for(k=k?k-1:k,j=SA[rnk[i]-1];a[i+k]==a[j+k];++k);
}
void init() {scanf(" %s",str);n = strlen(str);for(int i=0;i<n;++i) a[i+1] = str[i];
}
int main() {int T;scanf("%d",&T);while(T--) {init();Suffix();int ans = 0;for(int i=1;i<=n;++i) ans+=max(n-SA[i]+1-Height[i],0);printf("%d\n",ans);}return 0;
}

例6:最长回文子串

题意:给定一个字符串,求最长回文子串。
做法:穷举每一位,然后计算以这个字符为中心的最长回文子串。注意这里要分两种情况,一是回文子串的长度为奇数,二是长度为偶数。两种情况都可以转化为求一个后缀和一个反过来写的后缀的最长公共前缀。具体的做法是:将整个字符串反过来写在原字符串后面,中间用一个特殊的字符隔开。这样就把问题变为了求这个新的字符串的某两个后缀的最长公共前缀。

例7:连续重复子串

题意:给定一个字符串L,已知这个字符串是由某个字符串S重复R次而得到的,求R的最大值。
做法:穷举字符串S的长 k,然后判断是否满足。判断的时候,先看字符串L的长度能否被k整除,再看suffix(1)和suffix(k+1)的最长公共前缀是否等于n-k。在询问最长公共前缀的时候,suffix(1)是固定的,所以RMQ问题没有必要做所有的预处理,只需求出height数组中的每一个数到height[rank1]之间的最小值即可。整个做法的时间复杂度为O(n)。

转载于:https://www.cnblogs.com/RRRR-wys/p/9323318.html

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

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

相关文章

CF140C-New Year Snowmen【优先队列】

正题 题目链接:https://www.luogu.com.cn/problem/CF140C 题目大意 nnn个雪球&#xff0c;一个雪人需要用333个不同大小的雪球堆起&#xff0c;求最多雪人。 解题思路 我们每次拿相同雪球中最多的三个来堆即可&#xff0c;用优先队列维护。 时间复杂度O(nlog⁡n)O(n\log n)O…

基于.net standard 的动态编译实现

在前文[基于.net core 微服务的另类实现]结尾处&#xff0c;提到了如何方便自动的生成微服务的客户端代理&#xff0c;使对于调用方透明&#xff0c;同时将枯燥的东西使用框架集成&#xff0c;以提高使用便捷性。在尝试了基于 Emit 中间语言后&#xff0c;最终决定使用生成代码…

线段树动态开点区间加区间求和

线段树动态开点区间加区间求和 题目来源&#xff1a; 陕西师范大学第七届程序设计竞赛网络同步赛 H. 万恶的柯怡 思想&#xff1a; 保证叶子节点被完整的覆盖&#xff0c;需要开节点&#xff0c;就把左右儿子都开出来&#xff0c;其余和普通线段树一样。 tips&#xff1a; 用结…

初一模拟赛总结(6.6 my brother高考前一天,加油!(。・`ω´・。))

成绩&#xff1a; 注&#xff1a;rankrankrank是有算其他$dalao的 T1T1T1好像因为精度问题被卡了 rankrankranknamenamenamescorescorescoreT1T1T1T2T2T2T3T3T3T4T4T4111hkyhkyhky180180180100100100000808080000222whdwhdwhd130130130100100100000202020101010222lyflyflyf13…

P1337-[JSOI2004]平衡点/吊打XXX【模拟退火】

正题 题目链接:https://www.luogu.com.cn/problem/P1337 题目大意 nnn个点有重量wiw_iwi​&#xff0c;求重心。 解题思路 模拟退火随机找一个重心然后不断接近即可。 codecodecode #include<cstdio> #include<cstring> #include<algorithm> #include<…

基于阿里云 DNS API 实现的 DDNS 工具

0.简要介绍0.1 思路说明AliDDNSNet 是基于 .NET Core 开发的动态 DNS 解析工具&#xff0c;借助于阿里云的 DNS API 来实现域名与动态 IP 的绑定功能。工具核心就是调用了阿里云 DNS 的两个 API &#xff0c;一个 API 获取指定域名的所有解析记录&#xff0c;然后通过比对与当前…

【拓扑排序】【DP】奖金(ssl 1325)

奖金 ssl 1325 题目大意&#xff1a; 有n个人&#xff0c;某个人要比另外一个人的工资高&#xff08;工资最低为100&#xff0c;最少多1元&#xff09;&#xff0c;问最少发多少工资 原题&#xff1a; 题目描述 由于无敌的凡凡在2005年世界英俊帅气男总决选中胜出&#x…

网络流24题

网络流24题 说在前边 一直没有完整的刷过这套题&#xff0c;打算最近一点点刷掉通过《最小割模型在信息学竞赛中的应用》及《浅析一类最小割问题》学习常规建图技巧飞行员配对方案问题 二分图最大匹配 #include <bits/stdc.h> typedef long long ll; const int inf 0x3f…

P3959-宝藏【模拟退火】

正题 题目链接:https://www.luogu.com.cn/problem/P3959 题目大意 nnn个点&#xff0c;mmm条边&#xff0c;求一棵有根生成树权值最小。对于一条边(fa,x,w)(fa,x,w)(fa,x,w)会产生权值depfa∗wdep_{fa}*wdepfa​∗w。 解题思路 我们模拟退火每次随机一个序列&#xff0c;然后…

ASP.NET Core Web API 集成测试中使用 Bearer Token

在 ASP.NET Core Web API 集成测试一文中, 我介绍了ASP.NET Core Web API的集成测试. 在那里我使用了测试专用的Startup类, 里面的配置和开发时有一些区别, 例如里面去掉了用户身份验证相关的中间件.但是有些被测试的行为里面需要用到身份/授权信息.所以本文就介绍一下在API集成…

桐桐的雷达

桐桐的雷达 题目大意&#xff1a; 有一堆数字&#xff0c;并给出一个范围&#xff0c;判断不在范围内的数字是否多过10%&#xff0c;若不多过&#xff0c;那输出范围内数字的平均值 原题&#xff1a; 题目描述 桐桐在去广州的路上&#xff0c;对高速公路上的测速雷达产生了…

Codeforces Round #497 (Div. 1)

Codeforces Round #497 (Div. 1) A. Reorder the Array 先满足数值较小的位置&#xff0c;每次找恰好大于这个值的一个值即可。 #include <bits/stdc.h> #define rep(i,a,b) for(int ia;i<b;i) #define pb push_back typedef long long ll; const int N 100200; usin…

jzoj6824-[2020.10.17提高组模拟]英雄联盟【期望】

正题 题目大意 开始暴击率为xxx&#xff0c;每次失败后都会增加xxx&#xff0c;成功后重置&#xff0c;然后求攻击1010610^{10^6}10106次后的暴击次数除以1010610^{10^6}10106 解题思路 定义ansansans为期望攻击多少次后暴击&#xff0c;然后答案为1ans\frac{1}{ans}ans1​。…

【模拟】桐桐的游戏

桐桐的游戏 题目大意&#xff1a; 有一个用数组成的环要从1跳到z&#xff08;有些点不能跳&#xff09;&#xff0c;每次跳的步数最少有多少步 原题&#xff1a; 题目描述 桐桐最近在玩一个跳棋游戏&#xff0c;规则是&#xff1a;有个圆圈&#xff0c;分成N等分&#xff…

ASP.NET Core 应用发布与部署指南

一、前言本篇主要包含哪些内容&#xff1f;将项目发布到本地目录将项目传输到服务器并配置启动&开机自动启动将Nginx作为访问入口&#xff0c;配置反向代理本篇环境信息开发环境&#xff1a;用途工具&版本操作系统Windows 10开发工具Visual Studio 2017&#xff08;15.…

jzoj6826-[2020.10.17提高组模拟]隔膜【博弈论】

正题 题目大意 n∗nn*nn∗n的矩形&#xff0c;每一个人操作时如果棋盘上有一个k∗kk*kk∗k的矩形空地就可以选择一个点堵上。如果没有就失败了&#xff0c;求必胜方。 解题思路 如果场地上有一个位置堵上后即可堵上所有k∗kk*kk∗k的矩形那么这个点被堵住后就赢了&#xff0c;…

牛客网暑期ACM多校训练营(第一场)

牛客网暑期ACM多校训练营&#xff08;第一场&#xff09; A. Monotonic Matrix 考虑0和1的分界线&#xff0c;1和2的分界线&#xff0c;发现问题可以转化为两条不互相穿过的路径的方案数&#xff08;可重叠&#xff09;&#xff0c;题解的做法就是把一条路径斜着平移&#xff0…

2014 ACM/ICPC Asia Regional Xi'an Online

2014西安网络赛 A. Post Robot 把每种单词都kmp跑一遍&#xff0c;顺序输出即可 #include <cstdio> #include <iostream> #include <algorithm> #include <map> #include <cstring> #include <cmath> #include <queue> #include <…

MongoDB发布4.0版本,支持ACID事务

MongoDB最近发布了最新的4.0版本。毫无疑问&#xff0c;这一版本的主要特性是支持多文档ACID事务。MongoDB向与关系型数据库产品的融合迈出了一大步&#xff0c;现在支持会话的概念&#xff0c;并可以使用start_transaction()和commit_transaction()方法将多个数据库命令包含在…

P5491-[模板]二次剩余

正题 题目链接:https://www.luogu.com.cn/problem/P5491 题目大意 求解x2N(modP)x^2N(mod\ \ P)x2N(mod P) 解题思路 若aaa在模ppp意义下可以开根那么aaa就是ppp的二次剩余&#xff0c;定义 (ap){1(a是p的二次剩余)−1(a是p的二次非剩余)0(p∣a)\binom{a}{p}\left\{\begin{…