Manacher 最长回文字串的个数
public class Code_01_Manacher {
public static int maxPLen(String s){
if(s == null || s.length() == 0){
return 0;
}
char[] str = mancherString(s);//123->#1#2#3# 防止奇数偶数
int[] pArr = new int[str.length];
int R = -1;//第一个失败的位置,这个R前面都是正确的
int C = -1;
int max = Integer.MAX_VALUE;
for (int i = 0; i < str.length; i++) {
pArr[i] = i > R ? Math.min(pArr[2 * C - i],R-i):1;
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.min(max,pArr[i]);
}
return max -1;//返回的是半径,如果是直径除2
}
}
给定一个字符串,变成回文串,只能在后面添加回文串,后面添加最少
思路,找到第一个数的回文半径能到最右边,然后把前面的补上
public static String maxPLens(String s){
if(s == null || s.length() == 0){
return 0;
}
char[] str = mancherString(s);//123->#1#2#3# 防止奇数偶数
int[] pArr = new int[str.length];
int R = -1;//第一个失败的位置,这个R前面都是正确的
int C = -1;
int maxContainsEnd = -1;
for (int i = 0; i < str.length; i++) {
pArr[i] = i > R ? Math.min(pArr[2 * C - i],R-i):1;
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;
}
if(R == str.length){
maxContainsEnd = pArr[i];
break;
}
}
char[] res = new char[s.length() - maxContainsEnd + 1];
for (int i = 0; i < res.length; i++) {
res[res.length-1-i] = str[i * 2 + 1];
}
return String.valueOf(res);
}
假设一个固定大小为W的窗口,依次划过arr,返回每一个滑动窗口的最大值
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++){
while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]){
qmax.pollLast();
}
qmax.addLast(i);
//如果窗口不够w个,就弹出
if(qmax.peekFirst() == i - w){
qmax.pollFirst();
}
//以上窗口更新完了,收答案
if (i >= w - 1) {
res[index++] = qmax.peekFirst();
}
}
return res;
}
给定一个整形数组arr,和一个整数sum,某个arr中的子数组sub,如果想要达标,必须满足:
sub中的最大值-sub的最小值 <=sum。返回arr中达标子数组的个数
思路:如果一个数组符合,这个数组的所有子数组也符合,如果一个数组不符合,这个数组增长之后也不符合。
设置两个窗口,一个保持最大值,一个保持最小值,从0下标往后增加,一直到不符合的位置,然后知道了0为下标的结果,然后窗口从1开始,但是保持原来的窗口,就是把原来窗口的起始位置0变为1,这样得到1为下标符合条件的结果,重复
public static int getNum(int[] arr,int num){
if(arr == null || arr.length == 0){
return 0;
}
LinkedList<Integer> qmin = new LinkedList<>();
LinkedList<Integer> qmax = new LinkedList<>();
int L = 0;
int R = 0;
//窗口[L,R) [0,0)代表窗口一个数没没有 [0,1)表示只有0
int res = 0;
while(L < arr.length){
while(R < arr.length){
while(!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[R]){
qmin.pollLast();
}
qmin.addLast(R);
while(!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]){
qmax.pollLast();
}
qmax.addLast(R);
if(arr[qmax.getFirst()] - arr[qmin.getFirst()] > num){
break;
}
R++;
}
res += R - L;
if(qmin.peekFirst() == L){ //找到这次以L开头的结果后。L窗口要往后移动一个单位
qmin.pollFirst();
}
if(qmax.peekFirst() == L){
qmax.pollFirst();
}
L++;
}
return res;
}
单调栈,在一个数组中,这个数字,左边比这个数小且最近,右边的数比这个数小且最近
这个栈从顶到底部 保持从大到小排列,谁准备弹出,保存这个弹出左边最近且右边最近且最小的记录。
//返回数组的第一行代表 第一个位置左边距离它最小的 右边距离它最小的
public static int[][] getNearLess(int[] arr){
int[][] res = new int[arr.length][2];
//单调栈 存放下标组成的list 因为有重复的值 所以用list
Stack<List<Integer>> stack = new Stack<>();
for (int i = 0; i < arr.length; i++) {
//peek获取的是列表,要获取第一个0位置的元素
while(!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]){
List<Integer> popIs = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size()-1);
for(Integer popi : popIs){
res[popi][0] = leftLessIndex;
res[popi][1] = i;
}
//相等的 且比你小的
if(!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]){
stack.peek().add(Integer.valueOf(i));
}else{//不相等的 比你小的,或者栈为空 第一次进栈
ArrayList<Integer> list =new ArrayList<>();
list.add(i);
stack.push(list);
}
}
}
while(!stack.isEmpty()){
List<Integer> popIs = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size());
for(Integer popi : popIs){
res[popi][0] = leftLessIndex;
res[popi][1] = -1;
}
}
return res;
}
给定一个只包含正整数的数组arr,arr中任何一个子数组sub,一定可以算出(sub累加和)*(sub中的最小值)是什么,那么所有的子数组中,这个值最大是多少。
思路:找到以第一个位置为最小值的最长的数组,找到第二个位置最长的数组,,sub的累加和利用前缀和,前缀和就是从开始位置到当前位置的累加和, 根据前缀和可以求出任意长度数组的累加和,比如知道了1-L的和 知道了1-R的和 求L-R的累加和,用前面两个累加和相减就可以了。