后缀数组基础 Suffix Array

将字符串 \(s\) 的所有后缀按字典序排序。

SA 算法主要求以下数组:

  • \(\text{sa}_i\):排名为 \(i\) 的后缀的下标。

  • \(\text{rk}_i\):下标以 \(i\) 开始的排名。

  • \(\text{ht}_i\)\(\text{height}\) 数组。

    • \(\text{ht}_1 = 0,\text{ht}_i=\text{lcp}(\text{sa}[i],\text{sa}[i-1])\)\(\text{lcp}(i,j)\) 表示后缀 \(i\) 和后缀 \(j\) 的最长公共前缀的长度。
    • 即第 \(i\) 小的后缀与第 \(i-1\) 小的后缀的最长公共前缀的长度。

求 sa 和 rk

\(n = |s|\)

根据定义有 \(\text{sa}[\text{rk}[i]]=\text{rk}[\text{sa}[i]] = i\)

考虑倍增求:

\(\text{rk}_w[i]\) 为字符串 \(s[i:i+2^w-1]\) 在所有长度为 \(2^w\)\(s\) 的字串的排名。

可设 \(\text{rk}_0[i] = s[i]\)(此时是相对排名)。

设已经求出所有 \(\text{rk}_w[i]\),考虑求 \(\text{rk}_{w+1}[i]\)

  1. \(\text{rk}_w[i]\) 作为第一关键字,\(\text{rk}_w[i+2^w]\) 作为第二关键字进行排序,存到 \(\text{sa}\) 里。若 \(i+2^w > n\),可视 \(\text{rk}_{w}[i+2^w]\)\(0\),即最小。
  2. 通过 \(\text{sa}\) 反推出 \(\text{rk}_{w+1}[i]\)
  3. 不断重复上述过程,直到 \(2^w > n\)

最多会倍增 \(O(\log n)\) 次,每次排序的时间复杂度为 \(O(n\log n)\),总时间复杂度为 \(O(n\log^2 n)\)

考虑排序用基数排序 + 计数排序,这样每次排序的时间复杂度为 \(O(n)\),总时间复杂度为 \(O(n\log n)\)

/* OI wiki */
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>using namespace std;constexpr int N = 1000010;char s[N];
int n, sa[N], rk[N << 1], oldrk[N << 1], id[N], cnt[N];int main() {int i, m, p, w;scanf("%s", s + 1);n = strlen(s + 1);m = 127;for (i = 1; i <= n; ++i) ++cnt[rk[i] = s[i]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[rk[i]]--] = i;memcpy(oldrk + 1, rk + 1, n * sizeof(int));for (p = 0, i = 1; i <= n; ++i) {if (oldrk[sa[i]] == oldrk[sa[i - 1]]) {rk[sa[i]] = p;} else {rk[sa[i]] = ++p;}}for (w = 1; w < n; w <<= 1, m = n) {// 对第二关键字:id[i] + w进行计数排序memset(cnt, 0, sizeof(cnt));memcpy(id + 1, sa + 1,n * sizeof(int));  // id保存一份儿sa的拷贝,实质上就相当于oldsafor (i = 1; i <= n; ++i) ++cnt[rk[id[i] + w]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[rk[id[i] + w]]--] = id[i];// 对第一关键字:id[i]进行计数排序memset(cnt, 0, sizeof(cnt));memcpy(id + 1, sa + 1, n * sizeof(int));for (i = 1; i <= n; ++i) ++cnt[rk[id[i]]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[rk[id[i]]]--] = id[i];memcpy(oldrk + 1, rk + 1, n * sizeof(int));for (p = 0, i = 1; i <= n; ++i) {if (oldrk[sa[i]] == oldrk[sa[i - 1]] &&oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {rk[sa[i]] = p;} else {rk[sa[i]] = ++p;}}}for (i = 1; i <= n; ++i) printf("%d ", sa[i]);return 0;
}

但是上面代码常数太大,考虑进行一些常数优化。

  • 第二关键字排序优化

    其实上一轮的 \(\text{sa}\) 已经求出 \(\text{rk}_w[i+2^w]\) 的顺序,直接用就行。省略的对第二关键字的计数排序。

  • 计数排序值域优化

    每轮的排名都没排到 \(n\),记录一个 \(m\) 表示当前最大的排名,可以减少计数排序的常数。

  • 倍增次数优化

    若当前的排名排到了 \(n\),说明所有的后缀只用前 \(2^{w+1}\) 个字符就确定了顺序,再往后比较就没有意义了,此时结束算法即可。

最终代码:空间复杂度 \(O(n)\),时间复杂度\(O(n\log n)\),常数小

const int N = 1e6 + 5;
char s[N];
int n, sa[N], rk[N], tp[N], cnt[N];
void SA() {int m = 127;rep(i, 1, n) cnt[rk[i] = s[i]]++;rep(i, 1, m) cnt[i] += cnt[i - 1];per(i, 1, n) sa[cnt[rk[i]]--] = i;for(int w = 1, p; ; w <<= 1, m = p /* 计数排序值域优化 */){/* 第二关键字排序优化 */p = 0;rep(i, n - w + 1, n) tp[++p] = i;rep(i, 1, n) if(sa[i] > w) tp[++p] = sa[i] - w;/* 对第一关键字排序 */memset(cnt, 0, sizeof(cnt));rep(i, 1, n) cnt[rk[i]]++;rep(i, 1, m) cnt[i] += cnt[i - 1];per(i, 1, n) sa[cnt[rk[tp[i]]]--] = tp[i];/* 用 sa 反推 rk */memcpy(tp, rk, sizeof(tp));rk[sa[1]] = p = 1;rep(i, 2, n) rk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[min(sa[i] + w, n + 1)] == tp[min(sa[i - 1] + w, n + 1)]) ? p : ++p;if(p == n) break; /* 倍增次数优化 */}
}

求 height 数组

引理:\(\text{ht}[\text{rk}[i]] \ge \text{ht}[\text{rk}[i-1]]-1\)

证明:

\(\text{ht}[\text{rk}[i-1]] \le 1\),原式显然成立。

\(\text{ht}[\text{rk}[i-1]] > 1\)

考虑 \(\text{ht}[\text{rk}[i-1]]\)

height_array_1.png

将这两个字符串的第一个字符去掉:

height_array_2.png

橙色部分是可能多匹配的 \(\text{lcp}\)

因为后缀 \(\text{sa}[\text{rk}[i-1]-1]\) 和后缀 \(i - 1\)\(\text{lcp}\) 长度 \(>1\) ,则去掉一个字符后,后缀 \(\text{sa}[\text{rk}[i-1]+1]\) 还是还是排在后缀 \(i\) 前面。

后缀 \(\text{sa}[\text{rk}[i]-1]\) 是排在后缀 \(i\) 的前一个,又因为这是按字典序排的,所以后缀 \(\text{sa}[\text{rk}[i]-1]\) 和后缀 \(i\)\(\text{ht}[\text{rk}[i-1]]-1\) 个字符必定相同,即 \(\text{ht}[\text{rk}[i]] \ge \text{ht}[\text{rk}[i-1]]-1\)

证毕。

代码实现:时间复杂度 \(O(n)\)

for(int i = 1, k = 0; i <= n; i++)
{k -= !!k;while(s[i + k] == s[sa[rk[i] - 1] + k]) k++;ht[rk[i]] = k;
}

应用:

1. 求两个后缀的最大 \(\text{lcp}\) 长度\(\text{lcp}(\text{sa}[i],\text{sa}[j]) = \min\limits_{i\le k\le j}\{\text{ht}[k]\}\),可以结合树状数组。

2. 比较两个字串字典序大小:比较 \(A=s[a:b]\)\(B=s[c:d]\)

  • \(\text{lcp}(a,c) \ge \min(|A|,|B|), A < B \Leftrightarrow |A| < |B|\)\(A\)\(B\) 的前缀或 \(B\)\(A\) 的前缀。

  • \(\text{lcp}(a,c) < \min(|A|,|B|), A < B \Leftrightarrow \text{rk}[a] < \text{rk}[c]\)

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

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

相关文章

网站后台管理优化wordpress 搜索框 位置

仪表应用背景 电力运维行业&#xff1a;运维服务系统实时采集大量用户站的运行和动环数据&#xff0c;经专业数据分析&#xff0c;当用户站发生异常情况或运行故障时&#xff0c;及时反馈到运维指挥中心&#xff0c;并通过移动终端通知相应的运维工程师&#xff0c;指导现场作…

一般做企业网站需要什么资料wordpress网站有哪些

一、源码特点 java 商机管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

教师可以做网站吗顺德品牌网站建设

libcurl是C语言写成的网络编程工具库&#xff0c;asio是C写的网络编程的基础类型库 libcurl只用于客户端&#xff0c;asio既可以写客户端&#xff0c;也可以写服务端 libcurl实现了HTTP\FTP等应用层协议&#xff0c;但asio却只实现了传输层TCP/UDP等协议。 在学习http时介绍…

找能做网站的怎么做网站logo

在上一讲《Coursera自动驾驶课程第13讲&#xff1a;Least Squares》我们学习了最小二乘法相关知识。 本讲我们将学习20世纪最著名的一个算法&#xff1a;卡尔曼滤波。具体包括线性卡尔曼滤波&#xff08;KF&#xff09;&#xff0c;扩展卡尔曼滤波(EKF)&#xff0c;误差状态卡…

做网站怎么变现网站建设方案包括哪些内容

过犹不及——《论语先进》 大学考试时&#xff0c;有些老师允许带备cheet sheet&#xff08;忘纸条&#xff09;,上面记着关键公式和定义,帮助我们快速作答提高分数。传统的检索增强生成(RAG)方法也类似,试图找出精准的知识片段来辅助大语言模型(LLM)。 但这种方法其实有问题…

有什么做分销的几个网站网站如何加速

What’s more 山东大学 2020级数据库系统 实验一 山东大学 2020级数据库系统 实验二 山东大学 2020级数据库系统 实验三 山东大学 2020级数据库系统 实验四 山东大学 2020级数据库系统 实验五 山东大学 2020级数据库系统 实验六 山东大学 2020级数据库系统 实验七 山东大学 20…

戴尔网站建设成功做企业网站的缺点

else 操作 我们有简单的用户处理程序&#xff1a; func handleRequest(user *User) {if user ! nil {showUserProfilePage(user)} else {showLoginPage()} }如果没有提供用户&#xff0c;则需要将收到的请求重定向到登录页面。If else 似乎是个不错的决定。但我们的主要任务是…

网站颜色搭配实例参考消息官方网站阅读

文档链接&#xff1a;https://programmercarl.com/ LeetCode509.斐波那契数 题目链接&#xff1a;https://leetcode.cn/problems/fibonacci-number/ 思路&#xff1a; 动规五部曲&#xff1a; 这里我们要用一个一维dp数组来保存递归的结果 1.确定dp数组以及下标的含义 d…

广州做外贸网站建设四川建设网和四川省公共资源交易信息网

转自PaddleOCR docker模式 - 简书 目的: 公司要放弃第三方的ocr工具(日语),需要自己搭建训练一套,这篇是搭建 图片要标出文字的选取框 因为是日文所以ocr有专门的工具,只需要文字坐标就好如图 日文的账票需要加密一下 我得环境是 Ubuntu 22.04.1 LTS 1,下载代码 cd /hom…

吉首自治州住房和城乡建设局网站阅读网站怎么做

Overfitting and Regularization 1. 过拟合添加正则化2. 具有正则化的损失函数2.1 正则化线性回归的损失函数2.2 正则化逻辑回归的损失函数 3. 具有正则化的梯度下降3.1 使用正则化计算梯度&#xff08;线性回归 / 逻辑回归&#xff09;3.2 正则化线性回归的梯度函数3.3 正则化…

用一部手机制作网站网站建设文化策划方案

领取福利记得长按&#xff0c;领取技术书籍哦随着互联网大潮的到来&#xff0c;越来越多网站&#xff0c;应用系统需要海量数据的支撑&#xff0c;高并发、低延迟、高可用、高扩展等要求在传统的关系型数据库中已经得不到满足&#xff0c;或者说关系型数据库应对这些需求已经显…

移动端网站教程微信怎样将网站的内容做

本文分享一个南网上行通信规约20140617 报文解析软件 下载链接: https://pan.baidu.com/s/1ngbBG-yL8ucRWLDflqzEnQ 提取码: y1de 主界面如下图所示&#xff1a; 本软件同时支持南网上行通信规约20140617-Fn查询功能 软件同时支持多种规约类型&#xff0c;如&#xff1a;国网…

做漆包线的招聘网站做会计题目的网站

Python作为一种流行的高级编程语言&#xff0c;它的独特特性之一就是全局解释器锁&#xff08;Global Interpreter Lock&#xff0c;简称GIL&#xff09;。本文将深入探讨GIL的定义、工作原理以及对Python的影响&#xff0c;并介绍如何应对GIL的限制。 1. 什么是GIL&#xff1…

网站建设标书模版怎么做情侣网站

目录 1. 什么是Docker1.1. 什么是容器1.2. 什么是Docker 2. 安装Docker3. 镜像操作3.1. 拉取镜像3.2. 卸载镜像/容器3.3. 使用镜像/容器 4. 相关指令说明 1. 什么是Docker 1.1. 什么是容器 虚拟机&#xff1a; 操作系统是一个很笨重的程序&#xff0c;即是啥都不干&#xff0c…

绘画做动作的网站网站做pc

目录 背影 极限学习机 爬山算法优化遗传算法优化极限学习机的多分类预测,p-ga-elm多分类预测 主要参数 MATLAB代码 效果图 结果分析 展望 完整代码下载链接:爬山算法优化遗传算法优化极限学习机的多分类预测,p-ga-elm多分类预测(代码完整,数据)资源-CSDN文库 https://d…

为什么做pc网站网站搭建软件d

什么是ArkTS&#xff1f; ArkTS是一个为鸿蒙组件而生的框架&#xff0c;语法亲人好用。基于TypeScript&#xff0c;ArkTS拓展了声明式UI、状态管理等的能力&#xff0c;从本质上来讲&#xff0c;是TypeScript的扩展&#xff0c;主要服务于前端。 ArkTS的开发可以满足“一次开…

宝安响应式网站建设重庆市建设岗位培训中心

(PCWAP)装修设计公司网站模板 家装公司网站源码下载 PbootCMS内核开发的网站模板&#xff0c;该模板适用于装修设计、家装公司类等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b; PCWAP&#xff0c;同一个后台&#xff0c…

支付宝签约网站网站技能培训

描述&#xff1a; 有甲、乙两人&#xff0c;其中&#xff0c;甲只说假话&#xff0c;而不说真话&#xff1b;乙则是只说真话&#xff0c;不说假话。但是&#xff0c;他们两个人在回答别人的问题时&#xff0c;只通过点头与摇头来表示&#xff0c;不讲话。有一天&#xff0c;一个…

金融网站cms企业网站优化内容

介绍 文档中会进行SceneView的自定义扩展&#xff0c;实现显示常驻GUI和添加自定义叠加层&#xff08;Custom Overlay&#xff09;。 最近项目开发用回了原生的Unity UI相关内容。对于之前常用的FairyGUI来说&#xff0c;原生的UGUI对于UI同学来讲有些不太方便。再加上这次会…

大型美容网站建设额尔古纳做网站

智能优化算法应用&#xff1a;基于学生心理学算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于学生心理学算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.学生心理学算法4.实验参数设定5.算法…