后缀数组(算法竞赛进阶指南 P64,字符串 Hash + 二分答案)

本文主要是介绍后缀数组(算法竞赛进阶指南 P64,字符串 Hash + 二分答案),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.题目链接:

后缀数组

二.题目大意:

给字符串 s 的所有后缀按照字典序排序.

输出排序后的编号 和 排序后后缀数组 i 与 i - 1 的最大前缀长度.

三.分析:

如果直接用 sort 对每一个后缀排序,时间复杂度为 O(n^{2}logn),需要优化.

在 cmp 函数中,对于两个后缀 a,b 来讲,分别对应 s[a ~ n] 和 s[b ~ n].

那不妨求出第一个位置,使得 s[i] != s[j],\; 1\leq i < a < j \leq b.

这点可用字符串 Hash 前缀 + 二分答案实现.

这样每次只比较 1 个字符,排序函数的复杂度降到了 O(nlogn)

得到排序后的 SA 数组(记录编号)后,再同样二分前缀最大长度即可.

四.代码实现:

#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-8
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
using namespace std;const int M = (int)3e5;
const int mod = 99991;
const int inf = 0x3f3f3f3f;char s[M + 5];
int SA[M + 5];
ull P[M + 5];
ull sum[M + 5];int len;ull get_h(int l, int r)
{return sum[r] - sum[l - 1] * P[r - l + 1];
}int cal(int a, int b)
{int l = 0;int r = len - max(a, b) + 1;while(l < r){int mid = (l + r + 1) >> 1;if(get_h(a, a + mid - 1) == get_h(b, b + mid - 1))l = mid;elser = mid - 1;}return r;
}bool cmp(int a, int b)
{int x = cal(a, b);return s[a + x] < s[b + x];
}/**
ponoiiipoi
**/int main()
{scanf("%s", s + 1);len = strlen(s + 1);P[0] = 1;for(int i = 1; i <= len; ++i){SA[i] = i;P[i] = P[i - 1] * 131;sum[i] = sum[i - 1] * 131 + s[i] - 'a' + 1;}sort(SA + 1, SA + len + 1, cmp);for(int i = 1; i <= len; ++i)printf("%d%c", SA[i] - 1, i == len ? '\n' : ' ');for(int i = 1; i <= len; ++i)printf("%d%c", cal(SA[i - 1], SA[i]), i == len ? '\n' : ' ');return 0;
}

 

这篇关于后缀数组(算法竞赛进阶指南 P64,字符串 Hash + 二分答案)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

PyInstaller打包selenium-wire过程中常见问题和解决指南

《PyInstaller打包selenium-wire过程中常见问题和解决指南》常用的打包工具PyInstaller能将Python项目打包成单个可执行文件,但也会因为兼容性问题和路径管理而出现各种运... 目录前言1. 背景2. 可能遇到的问题概述3. PyInstaller 打包步骤及参数配置4. 依赖

MySQL更新某个字段拼接固定字符串的实现

《MySQL更新某个字段拼接固定字符串的实现》在MySQL中,我们经常需要对数据库中的某个字段进行更新操作,本文就来介绍一下MySQL更新某个字段拼接固定字符串的实现,感兴趣的可以了解一下... 目录1. 查看字段当前值2. 更新字段拼接固定字符串3. 验证更新结果mysql更新某个字段拼接固定字符串 -

Java String字符串的常用使用方法

《JavaString字符串的常用使用方法》String是JDK提供的一个类,是引用类型,并不是基本的数据类型,String用于字符串操作,在之前学习c语言的时候,对于一些字符串,会初始化字符数组表... 目录一、什么是String二、如何定义一个String1. 用双引号定义2. 通过构造函数定义三、St

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Nginx中配置HTTP/2协议的详细指南

《Nginx中配置HTTP/2协议的详细指南》HTTP/2是HTTP协议的下一代版本,旨在提高性能、减少延迟并优化现代网络环境中的通信效率,本文将为大家介绍Nginx配置HTTP/2协议想详细步骤,需... 目录一、HTTP/2 协议概述1.HTTP/22. HTTP/2 的核心特性3. HTTP/2 的优

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

在React中引入Tailwind CSS的完整指南

《在React中引入TailwindCSS的完整指南》在现代前端开发中,使用UI库可以显著提高开发效率,TailwindCSS是一个功能类优先的CSS框架,本文将详细介绍如何在Reac... 目录前言一、Tailwind css 简介二、创建 React 项目使用 Create React App 创建项目