Manacher算法、滑动窗口、单调栈(Java)


一、Manacher算法解决的问题

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

基本概念:
回文半径, 回文串长度的一半
回文直径:回文串长度
回文半径数组
之前所到达的最右回文右边界R
取得更远边界时,中心点的位置C

二、算法过程

  1. 第一种情况:当来到某一个中心点时,没有在最右回文右边界中,暴力扩。
  2. 第二种情况:当来到某一个中心点时,在最右回文右边界中,当前位置关于中心点对称的位置i、根据i的回文半径进行求解。在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

三、代码实现

package ManacherTestAndOther;

public class TestManacher {
	// 预处理,保证奇数和偶数的情况都能处理到
	public char[] ProcessString(String str) {
		char[] orignal = str.toCharArray();
		char[] ret = new char[str.length() * 2 + 1];
		int index = 0;
		for(int i=0; i<ret.length; ++i) {
			if((i & 1) == 1) {
				ret[i] = orignal[index++];
			}else {
				ret[i] = '#';
			}
		}
		return ret;
	}
	
	public String Manacher(String str) {
		if(str == null || str.length() == 0) return null;
		char[] s = ProcessString(str);
		int[] radius = new int[s.length];
		int C = -1;
		int R = -1;
		int Max = Integer.MIN_VALUE;
		int ans = -1;	// 记录最长回文串中心
		for(int i=0; i<s.length; ++i) {
			int len = i >= R? 1: Math.min(radius[2 * C - i], R - i);	// 不需要进行判断一定是回文串的回文半径
			int r = i + len;
			int l = i - len;
			while(l >= 0 && r < s.length) {
				if(s[l] == s[r]) {
					--l;
					++r;
				}else {
					break;
				}
			}
			if(r > R) {
				R = r;	// 更新最右回文半径
				C = i;	// 更新回文中心
			}
			radius[i] = r - i;	// 记录回文半径
			if(Max < radius[i]) {
				Max = radius[i];
				ans = i;
			}
		}
		if(ans == 1) {
			return ""+str.charAt(0)+"  ( len ="+(Max - 1)+")";
		}
		StringBuilder sBuilder = new StringBuilder();
		for(int i=ans - radius[ans]+1; i<ans + radius[ans]; ++i ) {
			if(s[i] != '#') {
				sBuilder.append(s[i]);
			}
		}
		return sBuilder.toString()+"  ( len ="+(Max - 1)+")";
	}
	
	public static void main(String[] args) {
		TestManacher tm = new TestManacher();
		String string = "1354513153";	// 51215
		System.out.println(string + ":" +tm.Manacher(string));
		string = "1345625";	
		System.out.println(string + ":" +tm.Manacher(string));
		string = "abscsdkkdscsba";	
		System.out.println(string + ":" +tm.Manacher(string));
		string = "dasdsaabscsdkkdscsbadfdda";	
		System.out.println(string + ":" +tm.Manacher(string));
		string = "4d5asd5115s";	
		System.out.println(string + ":" +tm.Manacher(string));
		string = "4d5asd515ds";	
		System.out.println(string + ":" +tm.Manacher(string));
	}
	
}

运行结果
在这里插入图片描述

四、滑动窗口

题目:窗口内最大最小值问题
在这里插入图片描述
思路:利用双端队列维护一个递减的数组

package ManacherTestAndOther;

import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;

public class MoveWindow {
	public int[] windows(int[] nums, int w) {
		if(nums == null || nums.length < w) {
			return null;
		}
		Deque<Integer> qmax = new LinkedList<>();	// 双端队列
		int[] ret = new int[nums.length - w + 1];
		int index = 0;
		for(int i=0; i<nums.length; ++i) {
			while(!qmax.isEmpty() && nums[qmax.peekLast()] < nums[i]) {	//维持队列中的递减顺序
				qmax.pollLast();
			}
			qmax.offerLast(i);
			if(qmax.peekFirst() == i - w) {
				qmax.pollFirst();
			}
			if(i >= w - 1) {
				ret[index++] = nums[qmax.peekFirst()];
			}
		}
		return ret;
	}
	
	public static void main(String[] args) {
		MoveWindow mw = new MoveWindow();
		System.out.println(Arrays.toString(mw.windows(new int[] {4, 3, 5, 4, 3, 3, 6, 7}, 3)));
	}
	
}

运行结果
在这里插入图片描述

五、单调栈

题目:单调栈
在这里插入图片描述

package ManacherTestAndOther;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;

public class MonotonicityStack {
	
	public int[][] MStack(int[] arr){
		int[][] ret = new int[2][arr.length];	// 用于存储答案ret[0]左边小的数 ret[1]右边小的数
		Arrays.fill(ret[0], Integer.MAX_VALUE);
		Arrays.fill(ret[1], Integer.MAX_VALUE);
		Stack<ArrayList<Integer>> stack = new Stack<>();	// ArrayList是放置出现相同的数字 递增栈
		for(int i=0; i<arr.length; ++i) {
			while(!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) {
				List<Integer> list = stack.pop();
				for(int num: list) {
					ret[1][num] = arr[i];	// 右侧小的数值
				}
			}
			if(!stack.isEmpty()) {
				List<Integer> list = stack.peek();
				if(arr[list.get(0)] == arr[i]) {
					ret[0][i] = ret[0][list.get(0)];	// 左边小于的和前面相等
					list.add(i);						// 加入当前同值队列
				}else {
					ret[0][i] = arr[list.get(list.size() - 1)];	// 左边最小值
					ArrayList<Integer> ele = new ArrayList<>();	// 加入栈中
					ele.add(i);
					stack.push(ele);
				}
			}else {
				ArrayList<Integer> ele = new ArrayList<>();	// 加入栈中
				ele.add(i);
				stack.push(ele);
			}
		}
		return ret;
	}
	
	public static void main(String[] args) {
		MonotonicityStack ms = new MonotonicityStack();
		int[] nums = new int[] {1, 5, 6, 7, 2, 0, 5, 7 ,5, 8};
		int[][] ans = ms.MStack(nums);
		System.out.println("原数组:"+Arrays.toString(nums));
		for(int i=0; i<nums.length; ++i) {
			System.out.print("左部最近的小于"+nums[i]+"的数:");
			System.out.print(ans[0][i] == Integer.MAX_VALUE? "无": ans[0][i]);
			System.out.print("		右部最近的小于"+nums[i]+"的数:");
			System.out.println(ans[1][i] == Integer.MAX_VALUE? "无": ans[1][i]);
		}
	}
	
}

运行结果
在这里插入图片描述

题目:
在这里插入图片描述
利用单调栈进行

package ManacherTestAndOther;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;

public class TheMaxIndexA {
	
	public int[] Process(int[] nums) {
		int[][] bounds = new int[2][nums.length];
		Arrays.fill(bounds[0], -1);
		Arrays.fill(bounds[1], nums.length);
		int max = Integer.MIN_VALUE;
		Stack<ArrayList<Integer>> stack = new Stack<>();
		for(int i=0; i<nums.length; ++i) {	// 以每个数为最小值,找到子数组的边界
			while(!stack.isEmpty() && nums[stack.peek().get(0)] > nums[i]) {
				List<Integer> list = stack.pop();
				for(int num: list) {
					bounds[1][num] = i;	// 设置右边界
				}
			}
			if(!stack.isEmpty()) {
				List<Integer> list = stack.peek();
				if(nums[list.get(0)] == nums[i]) {
					list.add(i);
					bounds[0][i] = bounds[0][list.get(0)];
				}else {
					ArrayList<Integer> ele = new ArrayList<>();
					ele.add(i);
					bounds[0][i] = list.get(list.size() - 1);
					stack.push(ele);
				}
			}else {
				ArrayList<Integer> ele = new ArrayList<>();
				ele.add(i);
				stack.push(ele);
			}
		}
		int ans = -1;	//用于记录答案
		for(int i=0; i<nums.length; ++i) {	// 计算最大指标A的值
			int temp = getNum(nums, bounds[0][i], bounds[1][i], nums[i]);
			if(max < temp) {
				max = temp;
				ans = i;
			}
		}
		return new int[] {max, bounds[0][ans], bounds[1][ans], nums[ans]};
	}
	
	/*
	 *  获取子数组指标A的值
	 *  num数组
	 *  l 子数组左边界
	 *  r 子数组右边界
	 *  min 子数组中的最小值
	 */	
	public int getNum(int[] num, int l, int r, int min) {
		int sum = 0;
		for(int i=l+1; i<r; ++i) {
			sum += num[i];
		}
		return min * sum;
	}
	
	public static void main(String[] args) {
		TheMaxIndexA indexA = new TheMaxIndexA();
		int[] arr = new int[] {1, 5, 6, 7, 2, 0, 5, 7 ,5, 8};
		int[] ans = indexA.Process(arr);
		System.out.println("原数组为:"+Arrays.toString(arr));
		System.out.println("指标A的最大值为:"+ans[0]);
		System.out.print("子数组为:");
		for(int i=ans[1]+1; i<ans[2]; ++i) {
			System.out.print(arr[i]+" ");
		}
		System.out.println();
		System.out.println("其中最小值为:"+ans[3]);
	}
	
}

运行结果
在这里插入图片描述


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值