左神算法笔记09:Manacher算法、窗口最大值更新结构、单调栈

Manacher算法

解决的问题:
字符串str中,最长回文子串的长度如何求解
如何做到时间复杂度O(N)完成

在这里插入图片描述

伪代码

在这里插入图片描述

在这里插入图片描述

时间复杂度分析

在这里插入图片描述

代码实现

public class Manacher {
    public static char[] manacherString(String s) {
        char[] charArr = s.toCharArray();
        char[] res = new char[s.length() * 2 + 1];
        int index = 0;
        for (int i = 0; i < res.length; ++i) {
            res[i] = (i & 1) == 0 ? '#' : s.charAt(index++);
        }
        return res;
    }

    public static int maxLengthOfPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }

        char[] str  = manacherString(s);
        //回文半径数组
        int[] pArr = new int[ str.length];
        // 整体最右回文右边界的下一个位置,最右的有效区是R-1位置,与讲解时不一样   L[...C...]R
        int R = -1;
        // 整体最右回文时的回文半径中心
        int C = -1;
        //扩出来的最大值
        int max = Integer.MIN_VALUE;

        //每个位置都求回文半径
        for (int i = 0; i <  str.length; ++i) {
            //不用验证也知道的i至少的回文区域,先给pArr[i],四种情况都对,可以验证
            //R <= i i在R外,至少不用验证的区域为1,对应情况1
            //R > i i在R内,至少不用验证的区域为为  i‘的位置对应的回文半径 和 R-i中 的最小值,对应 情况2 的1,2,3
            //i'的位置 = R - (R - C) * 2 + (R - i) = R - 2R + 2C + R - i = 2 * C - i
            pArr[i] = R > i ? Math.min(pArr[ 2 * C - i], R - i) : 1;

            //四种情况都往外扩,情况2的1 2 只会执行一次就会失败,这样写是为了省略代码,不需要if else判断多种情况
            while (i + pArr[i] <  str.length && i - pArr[i] > -1) {
                if (str[i + pArr[i]] == str[i - pArr[i]]) {
                    pArr[i]++;
                }
                else {
                    break;
                }
            }
            if (i + pArr[i] > R) {
                R = i + pArr[i];
                C = i;
            }
            max = Math.max(max, pArr[i]);
        }

        /*
         * 返回值s字符串的最大回文长度 等于改造的字符串的最大回文半径 - 1
         * eg.
         *    1221  ==> #1#2#2#1#   返回值为 5-1 = 4
         *    12321 ==> #1#2#3#2#1# 返回值为 6-1 = 5
         */
        return max - 1;
    }

    public static void main(String []args) {
        String s1 = "123";
        String s2 = "ab43534c1234321ab";

        System.out.println("s1: " + maxLengthOfPalindrome(s1));
        System.out.println("s2: " + maxLengthOfPalindrome(s2));
    }
}

窗口最大值更新结构

在这里插入图片描述
使用双端队列存索引,每次窗口右边界右移时,先看当前元素是否能加入双端队列尾部,不可以则弹出尾部元素直到可以加入,从而维持一个单调队列
双端队列维持的是当右边界不动,左边界右移的时候,依次判断是否跟队头元素相同从而弹出最大值

平均时间复杂度为O(1)

public class GetMaxNumInWindow {
 
    public static int[] getMaxNumInWin(int[] arr, int win){
        if(arr == null || arr.length < win || win < 1){
            return null;
        }
 
        LinkedList<Integer> maxQueue = new LinkedList<>();
        // 共有arr.length - win + 1个窗口,即共有arr.length - win + 1个最大值
        int[] res = new int[arr.length - win + 1];
        int index = 0;
        for(int i = 0; i < arr.length; i++){
            while(!maxQueue.isEmpty() && arr[maxQueue.peekLast()] <= arr[i]){
                /**
                 * 如果当前双端链表中的最后一个值比arr[i]小,则将其弹出,直到找到比arr[i]大的数,将arr[i]放在其后面,
                 * 如果maxQueue中没有比arr[i]大的数,那就将maxQueue中的数全部弹出,arr[i]放在头部
                 */
                maxQueue.pollLast();
            }
            maxQueue.addLast(i);
            if(maxQueue.peekFirst() == i - win){
                // 只有当窗口形成后才会有从双端队列头部失效一个数,即过期还是没过期
                maxQueue.pollFirst();
            }
            if(i >= win - 1){
                // 至少有一个窗口存在时,才有max
                res[index] = arr[maxQueue.peekFirst()];
            }
        }
        return res;
    }
}

单调栈

用途:想知道一个数左边比他大最近的是谁,右边比他大最近的是谁
在这里插入图片描述

应用

定义:正数数组中累积和与最小值的乘积,假设叫做指标A
给定一个数组,请返回子数组中,指标A最大的值

思路:以当前数字为最小值,求它的子数组,子数组选累加和最大的那个
由此可知是单调栈的做法
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值