Manacher 滑动窗口

1、Manacher算法:
字符串str中,最长回文子串的长度求解。

package hh;

public class Demo {
	public static int maxLcpsLength(String s) {
		if(s==null || s.length()==0) {
			return 0;
		}
		char[] str=manacherString(s);//添加"#"字符,变为#a#b#c#1……的形式
		int[] pArr=new int[str.length];//回文半径数组
		int r=-1;//回文最右边界再右一个位置
		int c=-1;//最右边界对应的回文中心
		int max=Integer.MIN_VALUE;//记录最大回文半径
		
		for(int i=0;i<pArr.length;i++) {//求每个位置的回文半径
			pArr[i]=i<r? Math.min(pArr[2*c-i],r-i) : 1;//1:自己本身构成回文
			
			while(i+pArr[i]<str.length && i-pArr[i]>0) {
				if(str[i+pArr[i]]==str[i-pArr[i]]) {
					pArr[i]++;
				}else {
					break;//不相等,以当前字符为中心不可能有更长的回文
				}
			}
			
			if(i+pArr[i]>r) {//i+pArr[i]为以当前字符为中心,回文右边界再往右一个位置
				r=i+pArr[i];
				c=i;
			}
			
			max=Math.max(max, pArr[i]);
		}
		
		return max-1;//最大回文半径-1才是最大回文长度
	}
	
	
	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 ? '#': charArr[index++];
		}
		
		return res;
	}
	public static void main(String[] args) {
		String s1="abc1234321ab";
		System.out.println(maxLcpsLength(s1));//7
		
		String s2="1234";
		
		System.out.println(maxLcpsLength(s2));//1
		
	}
}

2、窗口内最大值与最小值更新结构:

public static class WindowMax{
		private int l;//窗口左边界,l代表当前窗口中要过期的位
		private int r;//窗口右边界,r所在位代表的数并不在窗口中
		private int[] arr;
		private LinkedList<Integer> qmax;//存数组下标,任意时刻,窗口内最大值为双端队列头部代表值
		
		public WindowMax(int[] a) {
			arr=a;
			l=-1;
			r=0;
			qmax=new LinkedList<>();
		}
		
		public void addNumFromRight() {//窗口中加入新数(当前r所在位),r往右移动
			if(r==arr.length) {
				return;
			}
			
			while(!qmax.isEmpty() && arr[qmax.peekLast()]<=arr[r]) {
				//循环过程中出队行为可能导致队列变空,首先要保证队列非空,才可peekLast()
				qmax.pollLast();
			}
			
			qmax.addLast(r);
			r++;
		}
		
		public void removeNumFromLeft() {//l往右移动,l当前来到的位过期,不在窗口内了
			if(l>=r-1) {//l<r-1,窗口中才有数
				return;
			}
			
			l++;
			if(qmax.peekFirst()==l) {
				qmax.pollFirst();
			}
		}
		
		public Integer getMax() {
			if(!qmax.isEmpty()) {
				return arr[qmax.peekFirst()];
			}
			
			return null;
		}
	}

例题:有一个整型数组arr和一个大小为w的窗口从数组最左滑到最右,窗口每次向右滑一个位置。
在这里插入图片描述

public static int[] getMaxWindow(int[] arr,int w) {
		if(arr==null || w<1 || arr.length<w) {
			return null;
		}
		LinkedList<Integer> qmax=new LinkedList<>();
		int[] res=new int[arr.length-w+1];
		int index=0;
		
		for(int i=0;i<arr.length;i++) {//每个数都进、出qmax一次
			
			
			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;
	}
	
	public static void main(String[] args) {
		int[] arr= {4,3,5,4,3,3,6,7};
		System.out.println(Arrays.toString(getMaxWindow(arr,3)));//[5, 5, 5, 4, 6, 7]
		
	}

总代码:

package hh;

import java.util.*;

public class Demo {
	public static class WindowMax{
		private int l;//窗口左边界,l代表当前窗口中要过期的位
		private int r;//窗口右边界,r所在位代表的数并不在窗口中
		private int[] arr;
		private LinkedList<Integer> qmax;//存数组下标,任意时刻,窗口内最大值为双端队列头部代表值
		
		public WindowMax(int[] a) {
			arr=a;
			l=-1;
			r=0;
			qmax=new LinkedList<>();
		}
		
		public void addNumFromRight() {//窗口中加入新数(当前r所在位),r往右移动
			if(r==arr.length) {
				return;
			}
			
			while(!qmax.isEmpty() && arr[qmax.peekLast()]<=arr[r]) {
				//循环过程中出队行为可能导致队列变空,首先要保证队列非空,才可peekLast()
				qmax.pollLast();
			}
			
			qmax.addLast(r);
			r++;
		}
		
		public void removeNumFromLeft() {//l往右移动,l当前来到的位过期,不在窗口内了
			if(l>=r-1) {//l<r-1,窗口中才有数
				return;
			}
			
			l++;
			if(qmax.peekFirst()==l) {
				qmax.pollFirst();
			}
		}
		
		public Integer getMax() {
			if(!qmax.isEmpty()) {
				return arr[qmax.getFirst()];
			}
			
			return null;
		}
	}
	public static int[] getMaxWindow(int[] arr,int w) {
		if(arr==null || w<1 || arr.length<w) {
			return null;
		}
		LinkedList<Integer> qmax=new LinkedList<>();
		int[] res=new int[arr.length-w+1];
		int index=0;
		
		for(int i=0;i<arr.length;i++) {//每个数都进、出qmax一次
			
			
			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;
	}
	
	public static void main(String[] args) {
		int[] arr= {4,3,5,4,3,3,6,7};
		System.out.println(Arrays.toString(getMaxWindow(arr,3)));//[5, 5, 5, 4, 6, 7]
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值