22.1.23Manacher算法、双端队列、单调栈

1.Manacher算法

1)用途:
  • Manacher算法用于解决类似求某个字符串中最长的回文子串。(回文就是正着读和倒着读一样的结构)。

2)算法中的关键变量:
  • 回文半径 r:回文直径的一半;

  • 回文直径 d:整个回文的长度;

  • 之前扩大的所有位置中所到达的回文直径d的最右边界R;

  • 中心点c:取得R的那个点;

  • 回文半径数组:储存遍历字符串所得到的每个点的回文半径;

3)算法的流程:
  • 伪代码:

  • 这里的R与上述提到的概念不太相同,这里是回文最右边界的下一位。对字符串依次遍历,我们回遇到三种情况:

    • 当前遍历到的元素不在回文的有边界R内:此时没什么优化的方法,直接暴力扩大,判断扩大的是否为回文结构。

    • 当前遍历到的元素在右边界内:此时可以做出如下的位置关系:(L是R关于中心c的对称点,i'是i(当前遍历的点)的对称点),此时i的回文直径,回文半径都与他的对称点i'相同(根据对称的性质即可证明)。

    • 当前遍历到的元素的对称点的回文直径的左边界在L的左边:如下图,此时i的回文半径就是i~R所表示的区域。下面给出证明:x,y分别为L,L'的前一个数和后一个数,k,z也同理。由c扩得的回文区域是L~R所表示的区域,那我们就知道x!=z的,根据i'的会问区域的左边界是超过了L的,我们能知道:至少是x,y也在i'的回文区域内,所以x=y的,y=k,因为x!=z的,所以k!=z。

    • 当前遍历到的元素的对称点的回文直径的左边界刚好与L重合:如下图:此时i的回文半径就是i'的回文半径。对称可证。

  • code:

    public static char[] manacherString(String str) {
char[] charArr = str.toCharArray();
char[] res = new char[str.length() * 2 + 1];

int index = 0;
for (int i = 0; i != res.length; i++) {
res[i] = (i & 1) == 0 ? '#' : charArr[index++];
}
return res;
}

public static int maxLcpsLength(String str) {
if (str == null || str.length() == 0) {
return 0;
}
char[] charArr = manacherString(str);
int[] pArr = new int[charArr.length];
int C = -1;
int R = -1;
int max = Integer.MIN_VALUE;
for (int i = 0; i != charArr.length; i++) {
pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
//
while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {
if (charArr[i + pArr[i]] == charArr[i - pArr[i]])
pArr[i]++;
else {
break;
}
}
if (i + pArr[i] > R) {
R = i + pArr[i];
C = i;
}
max = Math.max(max, pArr[i]);
}
return max - 1;
}

public static void main(String[] args) {
String str1 = "abc1234321ab";
System.out.println(maxLcpsLength(str1));
}

2.双端队列

1)问题:
  • 给定一个输入int[] arr, 窗口大小为w,输出长度为n-w+1得到数组res,res中包含每次窗口中的最大值。

2)思路(进阶:L,R自由移动的情况):
  • 设置一个双端队列,前后都可以进出,当R右移时,队列后面进入一个数组元素的下标(一直数组通过下标可以得到这个数),双端队列中始终要维持单调(如果是求窗口最大值则保持从大到小的结构,反之求最小值就保持从小到大的结构)。

    • 当L右移时,双端队列就从前边淘汰一个元素。L对应的队列中的元素始终是L~R范围上的最大值。

    • 在R移动时,如果遇到当前要进入队列的数大于已经进入队列的数,则弹出小于等于当前数的数,遇到大于当前数或队列为空时在让当前数进入;

    • 如果当前数小于队列中的最后一个属就直接进入。

3)code:
    public static int[] getMaxWindow(int[] arr, int w) {
if (arr == null || w < 1 || arr.length < w) {
return null;
}
LinkedList<Integer> qmax = new LinkedList<Integer>();
int[] res = new int[arr.length - w + 1];
int index = 0;
for (int i = 0; i < arr.length; i++) {
while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]) {
qmax.pollLast();
}
qmax.addLast(i);
if (qmax.peekFirst() == i - w) {
qmax.pollFirst();
}
if (i >= w - 1) {
res[index++] = arr[qmax.peekFirst()];
}
}
return res;
}

// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}

public static void main(String[] args) {
int[] arr = { 4, 3, 5, 4, 3, 3, 6, 7 };
int w = 3;
printArray(getMaxWindow(arr, w));

}

3.单调栈

1)问题:
  • 求数组中任意一个数左右两边比他大(或小)且离他最近的两个数。

2)思路:
  • 在无重复值的情况下,设置一个栈结构,若求大的情况则栈中从栈底到栈顶的顺序为从大到小,反之求两边小的数的情况顺序就为从小到大。下面以求解左右两边较大的数来讨论。

  • 在一个数进站时,先判断栈是否为空或者当前的数是否小于栈顶的数,满足其中任意一种情况就直接压入栈,反之就从栈顶开始出栈,同时记录下出栈元素左边的距离他最近的且比它大的数是压在栈顶下面的数,右边的距离他最近的且比它大的数是即将入栈的数。重复操作直到栈为空或者遇到了比即将入栈的数大的数就停止操作,把即将入栈的数压入栈中。

  • 当遍历完是数组的最后一个元素时,开始清栈操作。具体的做法为一次弹出栈顶元素,记录左边的距离栈顶元素最近的且比它大的数是压在栈顶下面的数,右边的为空,直到栈空。

3)code:
//待补充

22.1.23Manacher算法、双端队列、单调栈的更多相关文章

  1. PAT 甲级 1074 Reversing Linked List (25 分)(链表部分逆置,结合使用双端队列和栈,其实使用vector更简单呐)

    1074 Reversing Linked List (25 分)   Given a constant K and a singly linked list L, you are supposed ...

  2. 双端队列(单调队列)poj2823 区间最小值(RMQ也可以)

    Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 41844   Accepted: 12384 ...

  3. Vijos1834 NOI2005 瑰丽华尔兹 动态规划 单调双端队列优化

    设dp[t][x][y]表示处理完前t个时间段,钢琴停留在(x,y)处,最多可以走多少个格子 转移时只需逆着当前倾斜的方向统计len个格子(len为时间区间的长度,len=t-s+1),如果遇到障碍就 ...

  4. 《算法实战策略》-chaper19-队列、栈和双端队列

    对于计算机专业的学生来说,他们一定会很熟悉一句话:程序设计 = 算法 + 数据结构.而根据笔者的理解,所谓程序设计其实就是为了编程解决实际问题,所谓算法是一种解决问题某种思维的方法,但是思维需要得到编 ...

  5. 算法-deque双端队列

    Python的deque模块,它是collections库的一部分.deque实现了双端队列,意味着你可以从队列的两端加入和删除元素 1.基本介绍 # 实例化一个deque对象d = deque()d ...

  6. STL-Deque(双端队列)与单调队列的实现

    前言: STl是个好东西,虽然他在不开O2的条件下会跑的很慢,但他着实会让你的代码可读性大大提高,令你的代码看起来既简单又整洁. 双端队列: 顾名思义,双端队列是有两个头的,一个队首指针,一个队尾指针 ...

  7. JavaScript 数据结构与算法2(队列和双端队列)

    学习数据结构的 git 代码地址: https://gitee.com/zhangning187/js-data-structure-study 1.队列和双端队列 队列和栈非常类似,但是使用了与 后 ...

  8. python 下的数据结构与算法---4:线形数据结构,栈,队列,双端队列,列表

    目录: 前言 1:栈 1.1:栈的实现 1.2:栈的应用: 1.2.1:检验数学表达式的括号匹配 1.2.2:将十进制数转化为任意进制 1.2.3:后置表达式的生成及其计算 2:队列 2.1:队列的实 ...

  9. 用Python实现的数据结构与算法:双端队列

    一.概述 双端队列(deque,全名double-ended queue)是一种具有队列和栈性质的线性数据结构.双端队列也拥有两端:队首(front).队尾(rear),但与队列不同的是,插入操作在两 ...

随机推荐

  1. [LeetCode]35.搜索插入位置(Java)

    原题地址: search-insert-position 题目描述: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会被按顺序插入的位置. 请必须使 ...

  2. RSA公私钥生成与使用

    参考 KeyStore 简述 Keytool 简述 Certificate Chain (证书链) 简述 详解RSA加密算法

  3. Dapr 中文社区汇总

    Dapr 于 2019 年在微软创建.随着时间的推移,许多社区成员加入该项目并做出贡献,扩展并帮助它在 2021 年 2 月达到了稳定的 1.0 版本.2021年3 月提交给 CNCF,在2021年1 ...

  4. Java笔记——选择语

    Java笔记--选择语句     1. if语句 规律: 1. 首先计算表达式的值. 2. 若表达式为真,则执行对应语句,为假则不执行.   第一种: if(表达式) 语句;//多个语句可用{} 例如 ...

  5. vue--前端路由及vue-router两种模式

    前言 路由这个概念最早在后端出现,随后前后端分离,直至当今的单页面应用,路由也在一直发生变化.本文来总结一下路由变化和vue-router中的路由模式区别相关知识点. 正文 1.什么是前端路由 (1) ...

  6. QT之HTTP

    概述 QT的HTTP操作都是异步的,内部通过线程实现. 相关类: QNetworkAccessManager [发送网络请求并接收响应] QNetworkReply [服务响应] QNetworkRe ...

  7. Write Combining Buffer

    现代CPU使用了很多技术来降低对内存存取数据的延时,因为CPU执行的速度实在是太快了,在从内存存取数据的约120ns中,可以执行数百条指令. 其中多级的缓存架构就是为了减少这种延时,来提高CPU的利用 ...

  8. Appium+ios环境搭建

    appium 环境搭建 安装homebrew(Mac OSX上的软件包管理工具) $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubuse ...

  9. SpringBoot入门一:基础知识(环境搭建、注解说明、创建对象方法、注入方式、集成jsp/Thymeleaf、logback日志、全局热部署、文件上传/下载、拦截器、自动配置原理等)

    SpringBoot设计目的是用来简化Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,SpringBoot致力于在蓬勃发 ...

  10. SQL从零到迅速精通【触发器】

    essay from:http://www.cnblogs.com/hoojo/archive/2011/07/20/2111316.html 触发器就类似一个小账本,记录了你在数据库做了哪些操作,可 ...