最详细的后缀数组

写在前面:

多余的我就不提了,只是觉得网上的博客吧流程,每个数组存的是下标还是值,都讲的不是很清楚(让我这种蒟蒻很是困扰)

相信到现在这种水平的都可以知道什么是倍增,为什么能倍增都比较清楚了,但是代码实现总感觉怪怪的…… 本文的定位就是想把代码实现的部分讲清楚…… 现在请听我娓娓道来……

清爽代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;const int MAX=1e6+5;
int n,m;
int tax[MAX],rak[MAX],tp[MAX],sa[MAX];
char s[MAX];void sort(int a[],int b[]){for(int i=0;i<=m;i++)tax[i]=0;for(int i=1;i<=n;i++)tax[a[i]]++;for(int i=1;i<=m;i++)tax[i]+=tax[i-1];for(int i=n;i>=1;i--)sa[tax[a[b[i]]]--]=b[i];
}bool comp(int r[],int a,int b,int k){return r[a]==r[b]&&r[a+k]==r[b+k];
}void get_sa(int a[],int b[]){for(int i=1;i<=n;i++)m=max(m,a[i]=s[i]-'0'),b[i]=i;sort(a,b);for(int p=0,j=1;p<n;j<<=1,m=p){p=0;for(int i=1;i<=j;i++)b[++p]=n-j+i;for(int i=1;i<=n;i++)if(sa[i]>j)b[++p]=sa[i]-j;sort(a,b);int *t=a;a=b;b=t;a[sa[1]]=p=1;for(int i=2;i<=n;i++)a[sa[i]]=comp(b,sa[i],sa[i-1],j)?p:++p;}
}int main(){scanf("%s",s+1);n=strlen(s+1);get_sa(rak,tp);for(int i=1;i<=n;i++)printf("%d ",sa[i]);
}

洛谷模板:后缀排序

一些定义:

看了上面的代码,你应该对后缀数组有了一个初步的印象吧(复杂、玄学而又美丽的代码)

我们先看看数组的定义:

  1. sa[i]=j ,表示第 i 名的后缀是从 j 开始的(注意存的是下标)
  2. rank[i]=j ,从 i 开始的后缀是第 j 名的(和 sa 互逆,是排名(值))
  3. tp[i] (b[i]) =j,第二关键字为 i 的后缀是从 j 开始的 (相当于是第二关键字的 sa 数组,存的是下标)
  4. tax[i]=j,表示第一关键字为 i 的数,有 j 个(基数排序时用的桶,是值)

知道了这些,相信你可以初步理解了吧(我都不信)

我们看看后缀排序的流程

这里写图片描述

这是算法的具体流程,但是为什么跟上面的代码似乎看不出来有什么联系……

我们把现在就把代码都解剖一下,细嚼慢咽食用更佳。

基数排序

当然你可以想到,我们是否能把关键字放到 pair 里,sort 一遍不就行了?

但是这样是 \(log^2\) 的,不是那么优秀,我们可以用一种比 sort 更快的方法——基数排序。

void sort(int a[],int b[]){for(int i=0;i<=m;i++)tax[i]=0;for(int i=1;i<=n;i++)tax[a[i]]++;for(int i=1;i<=m;i++)tax[i]+=tax[i-1];for(int i=n;i>=1;i--)sa[tax[a[b[i]]]--]=b[i];
}

别看他有这么一个高大尚的名字,其实他和桶排是同一个妈生的……

看代码:(n是字符串长度,m是数字的总数)

for(int i=0;i<=m;i++)tax[i]=0;

桶清零,不用说……

for(int i=1;i<=n;i++)tax[a[i]]++;

每个数放进各自的桶……

for(int i=1;i<=m;i++)tax[i]+=tax[i-1];

加上前边的桶的值,也就是现在这个值的排名……

for(int i=n;i>=1;i--)sa[tax[a[b[i]]]--]=b[i];

这行挺复杂
第一关键字(a 那层),第二关键字从大到小(b 那层),的桶内有多少个数(tax 那层),现在的这名,就是 b[i](sa 那层),然后桶内的数就减去一。

说的有点绕……这真心不好解释(可能是我的语言表达能力欠佳吧)

剩下的……

for(int p=0,j=1;p<n;j<<=1,m=p)

倍增嘛(又是这图)
这里写图片描述

for(int i=1;i<=j;i++)b[++p]=n-j+i;
for(int i=1;i<=n;i++)if(sa[i]>j)b[++p]=sa[i]-j;

这是更新第而关键字
第一个for,就是说后面越界的数关键字为零,当然排在前面。
第二个for,按排名,如果这个后缀在 j 后,就能做为 sa[i]-j 的第二关键字。

sort(a,b);
int *t=a;a=b;b=t;
a[sa[1]]=p=1;
for(int i=2;i<=n;i++)
a[sa[i]]=comp(b,sa[i],sa[i-1],j)?p:++p;

基数排序并更新第一关键字。
swap 一下只是因为不想多开一个数组,没有别的意思,现在 a 是新关键字,b 是旧关键字。
第一名的第一关键字肯定是 1 啦。
从二开始,如果现在的这名和前一名的两个关键字相等就是并列,否则就排到后一名。

comp 代码……

bool comp(int r[],int a,int b,int k){return r[a]==r[b]&&r[a+k]==r[b+k];
}

完了吗?

后缀排序的部分到这里就已经结束了……

但是光有个后缀排序有什么实质性的用处吗?

了解过的同学应该知道,有这么一个 height 数组,height[i] 表示的是,排名为 i 的后缀与排名为 i-1 的后缀的 LCP(最长公共前缀)

有了这个东东,就可以在后缀数组上乱搞,许多问题都能迎刃而解……

但怎么求是个问题,朴素的 \(n^2\) …… 算了吧

我们有这样一个性质 \(height[i+1] \ge height[i]-1\)

根据这样的性质,可以 \(O(n)\) 求 height

for(int i=1,j=0;i<=n;i++){if(j)j--;while(s[i+j]==s[sa[rak[i]-1]+j])j++;height[rak[i]]=j;
}

好了真的完了,这里不多说理论,目的只是填一下代码实现的坑,后缀数组的模板代码已经完了,希望有助于大家理解,再也不用痛苦的背板了……(卒)

转载于:https://www.cnblogs.com/ezoiLZH/p/9607849.html

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

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

相关文章

HTML5 Web 存储(localStorage和sessionStorage)

localStorage生命周期是永久&#xff0c;除非主动清除localStorage信息&#xff0c;否则这些信息将永远存在。存放数据大小为一般为5MB,而且它仅在客户端&#xff08;即浏览器&#xff09;中保存&#xff0c;不参与和服务器的通信。 // 1、保存数据到本地// 第一个参数是保存的…

面向对象之反射、包装、(定制)

什么是反射&#xff1f; 反射的概念是由Smith在1982年首次提出的&#xff0c;主要是指程序可以访问、检测和修改它本身状态或行为的一种能力&#xff08;自省&#xff09;&#xff0c; 这一概念的提出很快引发了计算机科学领域关于应用反射的研究。它首次被程序语言的设计领域所…

Error: Cannot find module 'webpack-cli'--解决方案

npm install webpack-cli -g 全局安装解决 今日赠语&#xff1a; 哈佛大学研究心理学表示&#xff1a; 1、床乱糟糟的人&#xff0c;比穿整洁的人&#xff0c;创造力平均要高出50% 2、经常迟到的人&#xff0c;比不迟到的人&#xff0c;幽默感平均要高出70% 3、饭量大的人&…

分享菜单效果

分享菜单效果&#xff1a; 1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset"UTF-8">5 <title>分享菜单</title>6 <style>7 #div1{width: 100px; height: …

vue axios解决post传参数问题

我相信遇到这个问题的兄弟们&#xff0c;不带参数的情况下都是没有问题吧&#xff0c; 如果有问题&#xff0c;百度吧&#xff0c;好解决&#xff0c;答案都比较靠谱 这里主要针对带参数的情况&#xff0c;坑多 另外&#xff0c;我默认你用postman带参测试接口是没问题的 不…

Spring Boot实践——基础和常用配置

借鉴&#xff1a;https://blog.csdn.net/j903829182/article/details/74906948 一、Spring Boot 启动注解说明 SpringBootApplication开启了Spring的组件扫描和Spring Boot的自动配置功能。实际上&#xff0c; SpringBootApplication将三个有用的注解组合在了一起。 Spring的Co…

[css] 什么是hack?css的hack有哪些?

[css] 什么是hack&#xff1f;css的hack有哪些&#xff1f; 一、总结 1、CSS hack&#xff1a;由于不同厂商的浏览器&#xff0c;比如Internet Explorer,Safari,Mozilla Firefox,Chrome等&#xff0c;或者是同一厂商的浏览器的不同版本&#xff0c;如IE6和IE7&#xff0c;对CS…

Element组件 Drawer 抽屉的关闭问题

场景 我使用的Drawer 抽屉是从上往下开的效果&#xff0c;点击搜索图标&#xff0c;从上往下开没问题&#xff0c;输入关键字搜索&#xff0c;搜索出来的列表放置于搜索栏下面&#xff0c;所以使用了一个子组件 问题就来了 搜出来的列表item&#xff0c;点击任意一条&#x…

First Steps with TensorFlow代码解析

注&#xff1a;本文的内容基本上都摘自tensorflow的官网&#xff0c;只不过官网中的这部分内容在国内访问不了&#xff0c;所以我只是当做一个知识的搬运工&#xff0c;同时梳理了一遍&#xff0c;方便大家查看。本文相关内容地址如下&#xff1a; https://developers.google.c…

宝塔nginx运行vue项目刷新404问题解决

我的项目是webpack构建的&#xff0c;因为我做一切开发都想要希望要从一个标准的构建去编码 所以&#xff0c;我的项目在node下运行&#xff0c;开发&#xff0c;调试是没有一点问题的&#xff0c;npm run build也是完全OK的&#xff0c;vue路由是history模式 把build出来的d…

vscode设置中文,设置中文不成功问题

刚安装好的vscode界面显示英文&#xff0c;如何设置中文呢&#xff1f; 在locale.json界面设置”locale":"zh-cn"也未能实现界面为中文&#xff0c;在网上找了参考了&#xff0c;以下教程真实测试有效&#xff01; 首先&#xff1a; 下载插件&#xff1a;Chines…

网页Request Headers请求头和Response Headers响应头

Request Headers Accept:告诉服务器&#xff0c;客户机支持的数据类型 Accept-Encoding:告诉服务器&#xff0c;客户机支持的数据压缩格式 Cache-Control&#xff1a;缓存控制&#xff0c;服务器通过控制浏览器要不要缓存数据 Connection:处理完这次请求&#xff0c;是断开…

springboot+jpa+mysql+redis+swagger整合步骤

springbootjpaMySQLswagger框架搭建好之上再整合redis&#xff1a; 在电脑上先安装redis&#xff1a; 一、在pom.xml中引入redis 二、在application.yml里配置redis&#xff0c;单独说明&#xff1a;redis刚一开始安装好是没有设置密码的。否则&#xff0c;会报connection错误。…

python3下使用requests实现模拟用户登录 —— 基础篇(马蜂窝)

我是从这篇博客中&#xff08;https://blog.csdn.net/zwq912318834/article/details/79571110&#xff09;了解的一点基础东西&#xff0c;代码都是从这篇博客里面的源代码直接复制过去测试和学习的。 遇到的问题&#xff1a; 1、返回状态码&#xff1a;502——百度得知这是一…

ACM-ICPC 2018 焦作赛区网络预赛 H题 String and Times(SAM)

Now you have a string consists of uppercase letters, two integers AA and BB. We call a substring wonderful substring when the times it appears in that string is between AA and BB (A \le times \le BA≤times≤B). Can you calculate the number of wonderful sub…

[css] css的height:100%和height:inherit之间有什么区别呢?

[css] css的height:100%和height:inherit之间有什么区别呢&#xff1f; 上周在微博上无节操吐槽了下inherit的段子&#xff0c;没想到回声还不少&#xff1a; 微博inherit无节操段子 不过inherit确实是个好东西&#xff0c;不仅节约代码&#xff0c;尤其与background之流打交…

http详解 请求报文格式和响应报文格式

题外话&#xff1a; 《Pi Network 免费挖矿国外热门项目 一个π币大约值3元到10元》相信过去BTC的人&#xff0c;信不信未来的PI&#xff0c;了解一下&#xff0c;唯一一个高度与之持平的项目 HTTP 工作原理 超文本传输协议(Hypertext Transfer Protocol&#xff0c;简称HTT…

【LeetCode】拓扑排序

【207】 Course Schedule 排课问题&#xff0c;n门课排课&#xff0c;有的课程必须在另外一些课程之前上&#xff0c;问能不能排出来顺序。 题解&#xff1a;裸的拓扑排序。参考代码见算法竞赛入门指南这本书。 1 class Solution {2 public:3 bool dfs(const vector<vec…

pycharm中更新pip版本的问题

经常使用Python的都知道pip&#xff0c;但有时候&#xff0c;下载某个模块不成功&#xff0c;提示信息如下 pytharm查看自带的pip版本 解决方式一&#xff1a; pytharm的terminal里卸载pip再安装pip 如果还不行&#xff0c;解决方式二 去你当前的项目路径下找到lib文件夹下的…

小程序的wx.onAccelerometerChange

https://www.2cto.com/kf/201802/724174.html&#xff08;copy&#xff09; 也许有人会问&#xff0c;小程序中都是竖直app形态&#xff0c;要横竖屏判断有什么用?即使判断出了横屏状态&#xff0c;你能把小程序横过来?答案是不能的&#xff0c;但是判断当前设备处于横屏或者…