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]
}
}