modify code

master
algorithmzuo 3 years ago
parent a19d982cf7
commit 64d7b5cd1f

@ -0,0 +1,74 @@
package 01_mca_01_about_sort;
public class Code01_MinSwapStep {
// 一个数组中只有两种字符'G'和'B'
// 可以让所有的G都放在左侧所有的B都放在右侧
// 或者可以让所有的G都放在右侧所有的B都放在左侧
// 但是只能在相邻字符之间进行交换操作,请问请问至少需要交换几次,
public static int minSteps1(String s) {
if (s == null || s.equals("")) {
return 0;
}
char[] str = s.toCharArray();
int step1 = 0;
int gi = 0;
for (int i = 0; i < str.length; i++) {
if (str[i] == 'G') {
step1 += i - (gi++);
}
}
int step2 = 0;
int bi = 0;
for (int i = 0; i < str.length; i++) {
if (str[i] == 'B') {
step2 += i - (bi++);
}
}
return Math.min(step1, step2);
}
// 可以让G在左或者在右
public static int minSteps2(String s) {
if (s == null || s.equals("")) {
return 0;
}
char[] str = s.toCharArray();
int step1 = 0;
int step2 = 0;
int gi = 0;
int bi = 0;
for (int i = 0; i < str.length; i++) {
if (str[i] == 'G') { // 当前的G去左边 方案1
step1 += i - (gi++);
} else {// 当前的B去左边 方案2
step2 += i - (bi++);
}
}
return Math.min(step1, step2);
}
// 为了测试
public static String randomString(int maxLen) {
char[] str = new char[(int) (Math.random() * maxLen)];
for (int i = 0; i < str.length; i++) {
str[i] = Math.random() < 0.5 ? 'G' : 'B';
}
return String.valueOf(str);
}
public static void main(String[] args) {
int maxLen = 100;
int testTime = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
String str = randomString(maxLen);
int ans1 = minSteps1(str);
int ans2 = minSteps2(str);
if (ans1 != ans2) {
System.out.println("Oops!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,29 @@
package 01_mca_01_about_sort;
// 测试链接https://leetcode.com/problems/first-missing-positive/
public class Code02_MissingNumber {
public static int firstMissingPositive(int[] arr) {
// l是盯着的位置
// 0 ~ L-1有效区
int L = 0;
int R = arr.length;
while (L != R) {
if (arr[L] == L + 1) {
L++;
} else if (arr[L] <= L || arr[L] > R || arr[arr[L] - 1] == arr[L]) { // 垃圾的情况
swap(arr, L, --R);
} else {
swap(arr, L, arr[L] - 1);
}
}
return L + 1;
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}

@ -0,0 +1,175 @@
package 01_mca_01_about_sort;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Code03_FindMinKth {
public static class MaxHeapComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
// 利用大根堆时间复杂度O(N*logK)
public static int minKth1(int[] arr, int k) {
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new MaxHeapComparator());
for (int i = 0; i < k; i++) {
maxHeap.add(arr[i]);
}
for (int i = k; i < arr.length; i++) {
if (arr[i] < maxHeap.peek()) {
maxHeap.poll();
maxHeap.add(arr[i]);
}
}
return maxHeap.peek();
}
// 改写快排时间复杂度O(N)
// k >= 1
public static int minKth2(int[] array, int k) {
int[] arr = copyArray(array);
return process2(arr, 0, arr.length - 1, k - 1);
}
public static int[] copyArray(int[] arr) {
int[] ans = new int[arr.length];
for (int i = 0; i != ans.length; i++) {
ans[i] = arr[i];
}
return ans;
}
// arr 第k小的数
// process2(arr, 0, N-1, k-1)
// arr[L..R] 范围上,如果排序的话(不是真的去排序)找位于index的数
// index [L..R]
public static int process2(int[] arr, int L, int R, int index) {
if (L == R) { // L = =R ==INDEX
return arr[L];
}
// 不止一个数 L + [0, R -L]
int pivot = arr[L + (int) (Math.random() * (R - L + 1))];
int[] range = partition(arr, L, R, pivot);
if (index >= range[0] && index <= range[1]) {
return arr[index];
} else if (index < range[0]) {
return process2(arr, L, range[0] - 1, index);
} else {
return process2(arr, range[1] + 1, R, index);
}
}
public static int[] partition(int[] arr, int L, int R, int pivot) {
int less = L - 1;
int more = R + 1;
int cur = L;
while (cur < more) {
if (arr[cur] < pivot) {
swap(arr, ++less, cur++);
} else if (arr[cur] > pivot) {
swap(arr, cur, --more);
} else {
cur++;
}
}
return new int[] { less + 1, more - 1 };
}
public static void swap(int[] arr, int i1, int i2) {
int tmp = arr[i1];
arr[i1] = arr[i2];
arr[i2] = tmp;
}
// 利用bfprt算法时间复杂度O(N)
public static int minKth3(int[] array, int k) {
int[] arr = copyArray(array);
return bfprt(arr, 0, arr.length - 1, k - 1);
}
// arr[L..R] 如果排序的话位于index位置的数是什么返回
public static int bfprt(int[] arr, int L, int R, int index) {
if (L == R) {
return arr[L];
}
// L...R 每五个数一组
// 每一个小组内部排好序
// 小组的中位数组成新数组
// 这个新数组的中位数返回
int pivot = medianOfMedians(arr, L, R);
int[] range = partition(arr, L, R, pivot);
if (index >= range[0] && index <= range[1]) {
return arr[index];
} else if (index < range[0]) {
return bfprt(arr, L, range[0] - 1, index);
} else {
return bfprt(arr, range[1] + 1, R, index);
}
}
// arr[L...R] 五个数一组
// 每个小组内部排序
// 每个小组中位数领出来组成marr
// marr中的中位数返回
public static int medianOfMedians(int[] arr, int L, int R) {
int size = R - L + 1;
int offset = size % 5 == 0 ? 0 : 1;
int[] mArr = new int[size / 5 + offset];
for (int team = 0; team < mArr.length; team++) {
int teamFirst = L + team * 5;
// L ... L + 4
// L +5 ... L +9
// L +10....L+14
mArr[team] = getMedian(arr, teamFirst, Math.min(R, teamFirst + 4));
}
// marr中找到中位数
// marr(0, marr.len - 1, mArr.length / 2 )
return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2);
}
public static int getMedian(int[] arr, int L, int R) {
insertionSort(arr, L, R);
return arr[(L + R) / 2];
}
public static void insertionSort(int[] arr, int L, int R) {
for (int i = L + 1; i <= R; i++) {
for (int j = i - 1; j >= L && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) (Math.random() * maxSize) + 1];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * (maxValue + 1));
}
return arr;
}
public static void main(String[] args) {
int testTime = 1000000;
int maxSize = 100;
int maxValue = 100;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
int k = (int) (Math.random() * arr.length) + 1;
int ans1 = minKth1(arr, k);
int ans2 = minKth2(arr, k);
int ans3 = minKth3(arr, k);
if (ans1 != ans2 || ans2 != ans3) {
System.out.println("Oops!");
}
}
System.out.println("test finish");
}
}

@ -0,0 +1,84 @@
package 01_mca_01_about_sort;
import java.util.Arrays;
import java.util.PriorityQueue;
public class Code04_CoverMax {
// [3,7]
// [0,10]
// [1,17]
public static int zuo(int[][] lines) {
// 根据开始位置排序
Arrays.sort(lines, (a, b) -> a[0] - b[0]);
PriorityQueue<Integer> heap = new PriorityQueue<>();
// 堆加入一个数字弹出一个数字时间复杂度O(log N)
int max = 0;
for (int[] line : lines) {
int start = line[0];
int end = line[1];
while (!heap.isEmpty() && heap.peek() <= start) {
heap.poll();
}
heap.add(end);
int curAns = heap.size();
max = Math.max(max, curAns);
}
return max;
}
public static int maxCover1(int[][] lines) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < lines.length; i++) {
min = Math.min(min, lines[i][0]);
max = Math.max(max, lines[i][1]);
}
int cover = 0;
for (double p = min + 0.5; p < max; p += 1) {
int cur = 0;
for (int i = 0; i < lines.length; i++) {
if (lines[i][0] < p && lines[i][1] > p) {
cur++;
}
}
cover = Math.max(cover, cur);
}
return cover;
}
// for test
public static int[][] generateLines(int N, int L, int R) {
int size = (int) (Math.random() * N) + 1;
int[][] ans = new int[size][2];
for (int i = 0; i < size; i++) {
int a = L + (int) (Math.random() * (R - L + 1));
int b = L + (int) (Math.random() * (R - L + 1));
if (a == b) {
b = a + 1;
}
ans[i][0] = Math.min(a, b);
ans[i][1] = Math.max(a, b);
}
return ans;
}
public static void main(String[] args) {
System.out.println("test begin");
int N = 100;
int L = 0;
int R = 200;
int testTimes = 200000;
for (int i = 0; i < testTimes; i++) {
int[][] lines = generateLines(N, L, R);
int ans1 = maxCover1(lines);
int ans2 = zuo(lines);
if (ans1 != ans2) {
System.out.println("Oops!");
}
}
System.out.println("test end");
}
}

@ -0,0 +1,35 @@
package 01_mca_01_about_sort;
// leetcode 875题
public class Code05_KokoEatingBananas {
public static int minEatingSpeed(int[] piles, int h) {
int L = 1;
int R = 0;
for (int pile : piles) {
R = Math.max(R, pile);
}
int ans = 0;
int M = 0;
while (L <= R) {
M = L + ((R - L) >> 1);
if (hours(piles, M) <= h) {
ans = M;
R = M - 1;
} else {
L = M + 1;
}
}
return ans;
}
public static int hours(int[] piles, int speed) {
int ans = 0;
int offset = speed - 1;
for (int pile : piles) {
ans += (pile + offset) / speed;
}
return ans;
}
}

@ -0,0 +1,143 @@
package 01_mca_02_about_coding;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
public class Code01_FindKMajority {
public static void water(int[] arr) {
if (arr == null || arr.length == 0) {
System.out.println("没有水王数");
} else {
int target = 0;
int hp = 0;
for (int cur : arr) {
if (hp == 0) {
target = cur;
hp = 1;
} else if (cur != target) {
hp--;
} else {
hp++;
}
}
if (hp == 0) {
System.out.println("没有水王数");
return;
}
int times = 0;
for (int cur : arr) {
if (target == cur) {
times++;
}
}
if (times > arr.length / 2) {
System.out.println("水王数是 : " + target);
} else {
System.out.println("没有水王数");
}
}
}
public static void printHalfMajor(int[] arr) {
int cand = 0;
int HP = 0;
for (int i = 0; i < arr.length; i++) {
if (HP == 0) {
cand = arr[i];
HP = 1;
} else if (arr[i] == cand) {
HP++;
} else {
HP--;
}
}
if (HP == 0) {
System.out.println("no such number.");
return;
}
HP = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == cand) {
HP++;
}
}
if (HP > arr.length / 2) {
System.out.println(cand);
} else {
System.out.println("no such number.");
}
}
public static void printKMajor(int[] arr, int K) {
if (K < 2) {
System.out.println("the value of K is invalid.");
return;
}
// 攒候选cands候选表最多K-1条记录 > N / K次的数字最多有K-1个
HashMap<Integer, Integer> cands = new HashMap<Integer, Integer>();
for (int i = 0; i != arr.length; i++) {
if (cands.containsKey(arr[i])) {
cands.put(arr[i], cands.get(arr[i]) + 1);
} else { // arr[i] 不是候选
if (cands.size() == K - 1) { // 当前数肯定不要每一个候选付出1点血量血量变成0的候选要删掉
allCandsMinusOne(cands);
} else {
cands.put(arr[i], 1);
}
}
}
// 所有可能的候选都在cands表中遍历一遍arr每个候选收集真实次数
HashMap<Integer, Integer> reals = getReals(arr, cands);
boolean hasPrint = false;
for (Entry<Integer, Integer> set : cands.entrySet()) {
Integer key = set.getKey();
if (reals.get(key) > arr.length / K) {
hasPrint = true;
System.out.print(key + " ");
}
}
System.out.println(hasPrint ? "" : "no such number.");
}
public static void allCandsMinusOne(HashMap<Integer, Integer> map) {
List<Integer> removeList = new LinkedList<Integer>();
for (Entry<Integer, Integer> set : map.entrySet()) {
Integer key = set.getKey();
Integer value = set.getValue();
if (value == 1) {
removeList.add(key);
}
map.put(key, value - 1);
}
for (Integer removeKey : removeList) {
map.remove(removeKey);
}
}
public static HashMap<Integer, Integer> getReals(int[] arr, HashMap<Integer, Integer> cands) {
HashMap<Integer, Integer> reals = new HashMap<Integer, Integer>();
for (int i = 0; i != arr.length; i++) {
int curNum = arr[i];
if (cands.containsKey(curNum)) {
if (reals.containsKey(curNum)) {
reals.put(curNum, reals.get(curNum) + 1);
} else {
reals.put(curNum, 1);
}
}
}
return reals;
}
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 1, 1, 2, 1 };
printHalfMajor(arr);
int K = 4;
printKMajor(arr, K);
}
}

@ -0,0 +1,81 @@
package 01_mca_02_about_coding;
public class Code02_EvenTimesOddTimes {
// arr中只有一种数出现奇数次
public static void printOddTimesNum1(int[] arr) {
int eor = 0;
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i];
}
System.out.println(eor);
}
// arr中有两种数出现奇数次
public static void printOddTimesNum2(int[] arr) {
int eor = 0;
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i];
}
// a 和 b是两种数
// eor != 0
// eor最右侧的1提取出来
// eor : 00110010110111000
// rightOne :00000000000001000
int rightOne = eor & (-eor); // 提取出最右的1
int onlyOne = 0; // eor'
for (int i = 0 ; i < arr.length;i++) {
// arr[1] = 111100011110000
// rightOne= 000000000010000
if ((arr[i] & rightOne) != 0) {
onlyOne ^= arr[i];
}
}
System.out.println(onlyOne + " " + (eor ^ onlyOne));
}
public static int bit1counts(int N) {
int count = 0;
// 011011010000
// 000000010000 1
// 011011000000
//
while(N != 0) {
int rightOne = N & ((~N) + 1);
count++;
N ^= rightOne;
// N -= rightOne
}
return count;
}
public static void main(String[] args) {
int a = 5;
int b = 7;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println(a);
System.out.println(b);
int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 };
printOddTimesNum1(arr1);
int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 };
printOddTimesNum2(arr2);
}
}

@ -0,0 +1,59 @@
package 01_mca_02_about_coding;
public class Code03_PrintMatrixZigZag {
public static void print(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return;
}
int leftUpRow = 0;
int leftUpCol = 0;
int rightDownRow = matrix.length - 1;
int rightDownCol = matrix[0].length - 1;
// 还没结束!
while (leftUpRow <= rightDownRow && leftUpCol <= rightDownCol) {
f(matrix, leftUpRow++, leftUpCol++, rightDownRow--, rightDownCol--);
}
}
// 打印框!
public static void f(int[][] matrix, int leftUpRow, int leftUpCol, int rightDownRow, int rightDownCol) {
if (leftUpRow == rightDownRow && leftUpCol == rightDownCol) {
System.out.print(matrix[leftUpRow][leftUpCol] + " ");
} else { // 不是一个数!
if (leftUpRow == rightDownRow) { // 当前的框是一条横线
for (int col = leftUpCol; col <= rightDownCol; col++) {
System.out.print(matrix[leftUpRow][col] + " ");
}
} else if (leftUpCol == rightDownCol) { // 当前的框是一条竖线
for (int row = leftUpRow; row <= rightDownRow; row++) {
System.out.print(matrix[row][leftUpCol] + " ");
}
} else { // 正常的框
for (int col = leftUpCol; col < rightDownCol; col++) {
System.out.print(matrix[leftUpRow][col] + " ");
}
for (int row = leftUpRow; row < rightDownRow; row++) {
System.out.print(matrix[row][rightDownCol] + " ");
}
for (int col = rightDownCol; col > leftUpCol; col--) {
System.out.print(matrix[rightDownRow][col] + " ");
}
for (int row = rightDownRow; row > leftUpRow; row--) {
System.out.print(matrix[row][leftUpCol] + " ");
}
}
}
}
public static void main(String[] args) {
int[][] matrix = {
{ 1, 2, 3},
{ 4, 5, 6},
{ 7, 8 ,9}
};
print(matrix);
}
}

@ -0,0 +1,43 @@
package 01_mca_02_about_coding;
public class Code04_ZigZagPrintMatrix {
public static void printMatrixZigZag(int[][] matrix) {
int tR = 0;
int tC = 0;
int dR = 0;
int dC = 0;
int endR = matrix.length - 1;
int endC = matrix[0].length - 1;
boolean fromUp = false;
while (tR != endR + 1) {
printLevel(matrix, tR, tC, dR, dC, fromUp);
tR = tC == endC ? tR + 1 : tR;
tC = tC == endC ? tC : tC + 1;
dC = dR == endR ? dC + 1 : dC;
dR = dR == endR ? dR : dR + 1;
fromUp = !fromUp;
}
System.out.println();
}
public static void printLevel(int[][] m,
int Arow, int ACol, int Brow, int Bcol, boolean f) {
if (f) {
while (Arow != Brow + 1) {
System.out.print(m[Arow++][ACol--] + " ");
}
} else {
while (Brow != Arow - 1) {
System.out.print(m[Brow--][Bcol++] + " ");
}
}
}
public static void main(String[] args) {
int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
printMatrixZigZag(matrix);
}
}

@ -0,0 +1,37 @@
package 01_mca_02_about_coding;
public class Code05_FindTheCelebrity {
// 提交时不要提交这个函数,因为默认系统会给你这个函数
// knows方法自己不认识自己
public static boolean knows(int x, int i) {
return true;
}
// 只提交下面的方法 0 ~ n-1
public int findCelebrity(int n) {
// 谁可能成为明星谁就是cand
int cand = 0;
for (int i = 0; i < n; ++i) {
if (knows(cand, i)) {
cand = i;
}
}
// cand是什么唯一可能是明星的人
// 下一步就是验证,它到底是不是明星
// 1) cand是不是不认识所有的人 cand...右侧cand都不认识
// 所以,只用验证 ....cand的左侧即可
for (int i = 0; i < cand; ++i) {
if (knows(cand, i)) {
return -1;
}
}
// 2) 是不是所有的人都认识cand
for (int i = 0; i < n; ++i) {
if (!knows(i, cand)) {
return -1;
}
}
return cand;
}
}

@ -0,0 +1,88 @@
package 01_mca_03_about_experience;
// 来自小红书
// 一个无序数组长度为n所有数字都不一样并且值都在[0...n-1]范围上
// 返回让这个无序数组变成有序数组的最小交换次数
public class Code01_MinSwapTimes {
// 纯暴力arr长度大一点都会超时
// 但是绝对正确
public static int minSwap1(int[] arr) {
return process1(arr, 0);
}
// 让arr变有序最少的交换次数是多少返回
// times, 之前已经做了多少次交换
public static int process1(int[] arr, int times) {
boolean sorted = true;
for (int i = 1; i < arr.length; i++) {
if (arr[i - 1] > arr[i]) {
sorted = false;
break;
}
}
if (sorted) {
return times;
}
// 数组现在是无序的状态!
if (times >= arr.length - 1) {
return Integer.MAX_VALUE;
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
swap(arr, i, j);
ans = Math.min(ans, process1(arr, times + 1));
swap(arr, i, j);
}
}
return ans;
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// 已知arr中只有0~n-1这些值并且都出现1次
public static int minSwap2(int[] arr) {
int ans = 0;
for (int i = 0; i < arr.length; i++) {
while (i != arr[i]) {
swap(arr, i, arr[i]);
ans++;
}
}
return ans;
}
// 为了测试
public static int[] randomArray(int len) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = i;
}
for (int i = 0; i < len; i++) {
swap(arr, i, (int) (Math.random() * len));
}
return arr;
}
public static void main(String[] args) {
int n = 6;
int testTime = 2000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * n) + 1;
int[] arr = randomArray(len);
int ans1 = minSwap1(arr);
int ans2 = minSwap2(arr);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,34 @@
package 01_mca_03_about_experience;
// 测试链接 : https://leetcode.com/problems/4-keys-keyboard/
public class Code02_4KeysKeyboard {
// 可以证明:
// 来到i的时候包括i在内最多有连续4次粘贴行为
// 不可能更多如果有连续5次粘贴一定就不再是最优解
// 假设开始时A的数量为S看如下的变化过程我们称这是行为一
// 开始 全选 复制(粘贴板S个A) 粘贴 粘贴 粘贴 粘贴 粘贴
// S S S 2*S 3*S 4*S 5*S 6*S
// 但是,注意看如下的行为二:
// 开始 全选 复制(粘贴板S个A) 粘贴 全选 复制(粘贴板2S个A) 粘贴 粘贴
// S S S 2*S 2*S 2*S 4*S 6*S
// 行为一经历8步最后是6*S个A
// 行为二经历8步最后是6*S个A
// 但是行为二在粘贴板上有2S个A而行为一在粘贴板上有S个A
// 所以行为一没有行为二优
// 以此说明来到i的时候包括i在内最多有连续4次粘贴行为
// 那么就尝试连续1次、连续2次、连续3次、连续4次粘贴行为即可
public static int maxA(int n) {
int[] dp = new int[n + 1];
for (int i = 1; i <= 6 && i <= n; i++) {
dp[i] = i;
}
for (int i = 7; i <= n; i++) {
dp[i] = Math.max(
Math.max(dp[i - 3] * 2, dp[i - 4] * 3),
Math.max(dp[i - 5] * 4, dp[i - 6] * 5));
}
return dp[n];
}
}

@ -0,0 +1,49 @@
package 01_mca_03_about_experience;
// 测试链接 : https://leetcode.com/problems/best-meeting-point/
public class Code03_BestMeetingPoint {
public static int minTotalDistance(int[][] grid) {
int N = grid.length;
int M = grid[0].length;
int[] iOnes = new int[N];
int[] jOnes = new int[M];
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1) {
iOnes[i]++;
jOnes[j]++;
}
}
}
int total = 0;
int i = 0;
int j = N - 1;
int iRest = 0;
int jRest = 0;
while (i < j) {
if (iOnes[i] + iRest <= iOnes[j] + jRest) {
total += iOnes[i] + iRest;
iRest += iOnes[i++];
} else {
total += iOnes[j] + jRest;
jRest += iOnes[j--];
}
}
i = 0;
j = M - 1;
iRest = 0;
jRest = 0;
while (i < j) {
if (jOnes[i] + iRest <= jOnes[j] + jRest) {
total += jOnes[i] + iRest;
iRest += jOnes[i++];
} else {
total += jOnes[j] + jRest;
jRest += jOnes[j--];
}
}
return total;
}
}

@ -0,0 +1,116 @@
package 01_mca_03_about_experience;
import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;
public class Code04_LowestLexicography {
public static String lowestString1(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
TreeSet<String> ans = process(strs);
return ans.size() == 0 ? "" : ans.first();
}
// strs中所有字符串全排列返回所有可能的结果
public static TreeSet<String> process(String[] strs) {
TreeSet<String> ans = new TreeSet<>();
if (strs.length == 0) {
ans.add("");
return ans;
}
for (int i = 0; i < strs.length; i++) {
String first = strs[i];
String[] nexts = removeIndexString(strs, i);
TreeSet<String> next = process(nexts);
for (String cur : next) {
ans.add(first + cur);
}
}
return ans;
}
// {"abc", "cks", "bct"}
// 0 1 2
// removeIndexString(arr , 1) -> {"abc", "bct"}
public static String[] removeIndexString(String[] arr, int index) {
int N = arr.length;
String[] ans = new String[N - 1];
int ansIndex = 0;
for (int i = 0; i < N; i++) {
if (i != index) {
ans[ansIndex++] = arr[i];
}
}
return ans;
}
public static class MyComparator implements Comparator<String> {
@Override
public int compare(String a, String b) {
return (a + b).compareTo(b + a);
}
}
public static String lowestString2(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
Arrays.sort(strs, new MyComparator());
String res = "";
for (int i = 0; i < strs.length; i++) {
res += strs[i];
}
return res;
}
// for test
public static String generateRandomString(int strLen) {
char[] ans = new char[(int) (Math.random() * strLen) + 1];
for (int i = 0; i < ans.length; i++) {
int value = (int) (Math.random() * 5);
ans[i] = (Math.random() <= 0.5) ? (char) (65 + value) : (char) (97 + value);
}
return String.valueOf(ans);
}
// for test
public static String[] generateRandomStringArray(int arrLen, int strLen) {
String[] ans = new String[(int) (Math.random() * arrLen) + 1];
for (int i = 0; i < ans.length; i++) {
ans[i] = generateRandomString(strLen);
}
return ans;
}
// for test
public static String[] copyStringArray(String[] arr) {
String[] ans = new String[arr.length];
for (int i = 0; i < ans.length; i++) {
ans[i] = String.valueOf(arr[i]);
}
return ans;
}
public static void main(String[] args) {
int arrLen = 6;
int strLen = 5;
int testTimes = 10000;
System.out.println("test begin");
for (int i = 0; i < testTimes; i++) {
String[] arr1 = generateRandomStringArray(arrLen, strLen);
String[] arr2 = copyStringArray(arr1);
if (!lowestString1(arr1).equals(lowestString2(arr2))) {
for (String str : arr1) {
System.out.print(str + ",");
}
System.out.println();
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}

@ -0,0 +1,48 @@
package 01_mca_03_about_experience;
import java.util.HashMap;
public class Code06_SetAll {
public static class MyValue<V> {
public V value;
public long time;
public MyValue(V v, long t) {
value = v;
time = t;
}
}
public static class MyHashMap<K, V> {
private HashMap<K, MyValue<V>> map;
private long time;
private MyValue<V> setAll;
public MyHashMap() {
map = new HashMap<>();
time = 0;
setAll = new MyValue<V>(null, -1);
}
public void put(K key, V value) {
map.put(key, new MyValue<V>(value, time++));
}
public void setAll(V value) {
setAll = new MyValue<V>(value, time++);
}
public V get(K key) {
if (!map.containsKey(key)) {
return null;
}
if (map.get(key).time > setAll.time) {
return map.get(key).value;
} else {
return setAll.value;
}
}
}
}

@ -0,0 +1,65 @@
package 01_mca_03_about_experience;
import java.util.HashMap;
public class Code07_InsertDeleteGetRandom {
public class RandomizedSet {
// 600 -> 0
private HashMap<Integer, Integer> keyIndexMap;
// 0 -> 600
private HashMap<Integer, Integer> indexKeyMap;
private int size;
public RandomizedSet() {
keyIndexMap = new HashMap<Integer, Integer>();
indexKeyMap = new HashMap<Integer, Integer>();
size = 0;
}
public boolean insert(int key) {
if (!keyIndexMap.containsKey(key)) {
keyIndexMap.put(key, size);
indexKeyMap.put(size++, key);
return true;
}
return false;
}
public boolean remove(int val) {
// c -> 2
// remove c
// 最后的位置9 - 1
// deleteIndex = 2;
// lastIndex = 8
// 8 -> z
// z -> 2
// 2 -> c(z)
// c -> X
// 8 -> z X
if (keyIndexMap.containsKey(val)) {
int deleteIndex = keyIndexMap.get(val);
int lastIndex = --size;
int lastKey = indexKeyMap.get(lastIndex);
keyIndexMap.put(lastKey, deleteIndex);
indexKeyMap.put(deleteIndex, lastKey);
keyIndexMap.remove(val);
indexKeyMap.remove(lastIndex);
return true;
}
return false;
}
public int getRandom() {
if (size == 0) {
return -1;
}
int randomIndex = (int) (Math.random() * size);
return indexKeyMap.get(randomIndex);
}
}
}

@ -0,0 +1,52 @@
package 01_mca_03_about_experience;
// 测试链接 : https://leetcode.com/problems/maximum-gap/
public class Code08_MaxGap {
public static int maximumGap(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
int N = nums.length;
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < N; i++) {
min = Math.min(min, nums[i]);
max = Math.max(max, nums[i]);
}
if (min == max) {
return 0;
}
// N 个数 N + 1个桶
// 0~9 min max boolean
// 10~19 min max boolean
boolean[] hasNum = new boolean[N + 1];
int[] maxs = new int[N + 1];
int[] mins = new int[N + 1];
int bid = 0;
for (int i = 0; i < N; i++) {
// 17 0~99 10个桶算一下我该去哪个桶
bid = bucket(nums[i], N, min, max);
mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i];
maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i];
hasNum[bid] = true;
}
int res = 0;
int lastMax = maxs[0];
int i = 1;
// 当前不空的桶min - 之前不空的桶max
// 把差值最大的记录!
for (; i <= N; i++) {
if (hasNum[i]) {
res = Math.max(res, mins[i] - lastMax);
lastMax = maxs[i];
}
}
return res;
}
public static int bucket(long num, long len, long min, long max) {
return (int) ((num - min) * len / (max - min));
}
}

@ -0,0 +1,58 @@
package 01_mca_04_about_data_structure;
// 来自网易
// 给定一个正数数组arr表示每个小朋友的得分
// 任何两个相邻的小朋友,如果得分一样,怎么分糖果无所谓,但如果得分不一样,分数大的一定要比分数少的多拿一些糖果
// 假设所有的小朋友坐成一个环形,返回在不破坏上一条规则的情况下,需要的最少糖果数
public class Code01_CircleCandy {
public static int minCandy(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return 1;
}
int n = arr.length;
int minIndex = 0;
for (int i = 0; i < n; i++) {
if (arr[i] <= arr[lastIndex(i, n)] && arr[i] <= arr[nextIndex(i, n)]) {
minIndex = i;
break;
}
}
int[] nums = new int[n + 1];
for (int i = 0; i <= n; i++, minIndex = nextIndex(minIndex, n)) {
nums[i] = arr[minIndex];
}
int[] left = new int[n + 1];
left[0] = 1;
for (int i = 1; i <= n; i++) {
left[i] = nums[i] > nums[i - 1] ? (left[i - 1] + 1) : 1;
}
int[] right = new int[n + 1];
right[n] = 1;
for (int i = n - 1; i >= 0; i--) {
right[i] = nums[i] > nums[i + 1] ? (right[i + 1] + 1) : 1;
}
int ans = 0;
for (int i = 0; i < n; i++) {
ans += Math.max(left[i], right[i]);
}
return ans;
}
public static int nextIndex(int i, int n) {
return i == n - 1 ? 0 : (i + 1);
}
public static int lastIndex(int i, int n) {
return i == 0 ? (n - 1) : (i - 1);
}
public static void main(String[] args) {
int[] arr = { 3, 4, 2, 3, 2 };
System.out.println(minCandy(arr));
}
}

@ -0,0 +1,91 @@
package 01_mca_04_about_data_structure;
public class Code02_LongestSumSubArrayLengthInPositiveArray {
public static int getMaxLength(int[] arr, int K) {
if (arr == null || arr.length == 0 || K <= 0) {
return 0;
}
int left = 0;
int right = 0;
int sum = arr[0];
int len = 0;
while (right < arr.length) {
if (sum == K) {
len = Math.max(len, right - left + 1);
sum -= arr[left++];
} else if (sum < K) {
right++;
if (right == arr.length) {
break;
}
sum += arr[right];
} else {
sum -= arr[left++];
}
}
return len;
}
// for test
public static int right(int[] arr, int K) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
if (valid(arr, i, j, K)) {
max = Math.max(max, j - i + 1);
}
}
}
return max;
}
// for test
public static boolean valid(int[] arr, int L, int R, int K) {
int sum = 0;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
return sum == K;
}
// for test
public static int[] generatePositiveArray(int size, int value) {
int[] ans = new int[size];
for (int i = 0; i != size; i++) {
ans[i] = (int) (Math.random() * value) + 1;
}
return ans;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int len = 50;
int value = 100;
int testTime = 500000;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generatePositiveArray(len, value);
int K = (int) (Math.random() * value) + 1;
int ans1 = getMaxLength(arr, K);
int ans2 = right(arr, K);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println("K : " + K);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("test end");
}
}

@ -0,0 +1,143 @@
package 01_mca_04_about_data_structure;
import java.util.HashMap;
//
public class Code03_LongestSumSubArrayLength {
public static int sumTargetMaxLength(int[] arr, int target) {
if(arr == null || arr.length == 0) {
return 0;
}
// 0~4 100
// (100, 4)
// 0~8 100
// (100, 4)
// key :某个前缀和
// value : 最早出现的位置!
HashMap<Integer, Integer> preSumEarlyMap = new HashMap<>();
// 一个数也没有的时候有前缀和是0
preSumEarlyMap.put(0, -1);
int sum = 0; // 0 ~ i 整体前缀和!
int ans = 0;
for(int i = 0; i < arr.length;i++) {
sum += arr[i];
int findPreSum = sum - target;
if(preSumEarlyMap.containsKey(findPreSum)) {
int earlyIndex = preSumEarlyMap.get(findPreSum);
int iendAns = i - earlyIndex;
ans = Math.max(ans, iendAns);
}
if(!preSumEarlyMap.containsKey(sum)) {
preSumEarlyMap.put(sum, i);
}
}
return ans;
}
public static int maxLength(int[] arr, int k) {
if (arr == null || arr.length == 0) {
return 0;
}
// key:前缀和
// value : 0~value这个前缀和是最早出现key这个值的
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(0, -1); // important
int len = 0;
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
if (map.containsKey(sum - k)) {
len = Math.max(i - map.get(sum - k), len);
}
if (!map.containsKey(sum)) {
map.put(sum, i);
}
}
return len;
}
// for test
public static int right(int[] arr, int K) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
if (valid(arr, i, j, K)) {
max = Math.max(max, j - i + 1);
}
}
}
return max;
}
// for test
public static boolean valid(int[] arr, int L, int R, int K) {
int sum = 0;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
return sum == K;
}
// for test
public static int[] generateRandomArray(int size, int value) {
int[] ans = new int[(int) (Math.random() * size) + 1];
for (int i = 0; i < ans.length; i++) {
ans[i] = (int) (Math.random() * value) - (int) (Math.random() * value);
}
return ans;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int len = 50;
int value = 100;
int testTime = 500000;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(len, value);
int K = (int) (Math.random() * value) - (int) (Math.random() * value);
int ans1 = maxLength(arr, K);
int ans2 = right(arr, K);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println("K : " + K);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("test end");
}
}

@ -0,0 +1,67 @@
package 01_mca_04_about_data_structure;
import java.util.HashMap;
// 来自京东
// 把一个01字符串切成多个部分要求每一部分的0和1比例一样同时要求尽可能多的划分
// 比如 : 01010101
// 01 01 01 01 这是一种切法0和1比例为 1 : 1
// 0101 0101 也是一种切法0和1比例为 1 : 1
// 两种切法都符合要求但是那么尽可能多的划分为第一种切法部分数为4
// 比如 : 00001111
// 只有一种切法就是00001111整体作为一块那么尽可能多的划分部分数为1
// 给定一个01字符串str假设长度为N要求返回一个长度为N的数组ans
// 其中ans[i] = str[0...i]这个前缀串要求每一部分的0和1比例一样同时要求尽可能多的划分下部分数是多少
// 输入: str = "010100001"
// 输出: ans = [1, 1, 1, 2, 1, 2, 1, 1, 3]
public class Code04_Ratio01Split {
// 001010010100...
public static int[] split(int[] arr) {
// key : 分子
// value : 属于key的分母表, 每一个分母,及其 分子/分母 这个比例,多少个前缀拥有
HashMap<Integer, HashMap<Integer, Integer>> pre = new HashMap<>();
int n = arr.length;
int[] ans = new int[n];
int zero = 0; // 0出现的次数
int one = 0; // 1出现的次数
for (int i = 0; i < n; i++) {
if (arr[i] == 0) {
zero++;
} else {
one++;
}
if (zero == 0 || one == 0) {
ans[i] = i + 1;
} else { // 0和1都有数量 -> 最简分数
int gcd = gcd(zero, one);
int a = zero / gcd;
int b = one / gcd;
// a / b 比例,之前有多少前缀拥有? 3+1 4 5+1 6
if (!pre.containsKey(a)) {
pre.put(a, new HashMap<>());
}
if (!pre.get(a).containsKey(b)) {
pre.get(a).put(b, 1);
} else {
pre.get(a).put(b, pre.get(a).get(b) + 1);
}
ans[i] = pre.get(a).get(b);
}
}
return ans;
}
public static int gcd(int m, int n) {
return n == 0 ? m : gcd(n, m % n);
}
public static void main(String[] args) {
int[] arr = { 0, 1, 0, 1, 0, 1, 1, 0 };
int[] ans = split(arr);
for (int i = 0; i < ans.length; i++) {
System.out.print(ans[i] + " ");
}
}
}

@ -0,0 +1,143 @@
package 01_mca_04_about_data_structure;
import java.util.HashMap;
// 本题测试链接 : https://leetcode.com/problems/lru-cache/
public class Code05_LRUCache {
public class LRUCache {
public LRUCache(int capacity) {
cache = new MyCache(capacity);
}
private MyCache cache;
public int get(int key) {
return cache.get(key);
}
public void put(int key, int value) {
cache.set(key, value);
}
public class Node {
public int key;
public int value;
public Node last;
public Node next;
public Node(int k, int v) {
key = k;
value = v;
}
}
// 自己要实现的双向链表!
public class NodeDoubleLinkedList {
private Node head;
private Node tail;
public NodeDoubleLinkedList() {
head = null;
tail = null;
}
// 让你加一个节点!往尾巴上加!
public void addNode(Node newNode) {
if (newNode == null) {
return;
}
if (head == null) {
head = newNode;
tail = newNode;
} else {
tail.next = newNode;
newNode.last = tail;
tail = newNode;
}
}
// 一定能保证x就在双向链表上
// 请把x之前和x之后的节点之间重连抠出x
// 把x放到尾巴上去
public void moveNodeToTail(Node x) {
if (tail == x) {
return;
}
// x不是尾巴x右边一定是有节点的不为空
if (head == x) { // 前边没有节点x直接去尾巴head要往下走
head = x.next;
head.last = null;
} else { // x有前也有后
x.last.next = x.next;
x.next.last = x.last;
}
x.last = tail;
x.next = null;
tail.next = x;
tail = x;
}
public Node removeHead() {
if (head == null) {
return null;
}
Node res = head;
if (head == tail) {
head = null;
tail = null;
} else {
head = res.next;
res.next = null;
head.last = null;
}
return res;
}
}
public class MyCache {
private HashMap<Integer, Node> keyNodeMap;
private NodeDoubleLinkedList nodeList;
private final int capacity;
public MyCache(int cap) {
keyNodeMap = new HashMap<>();
nodeList = new NodeDoubleLinkedList();
capacity = cap;
}
public int get(int key) {
if (keyNodeMap.containsKey(key)) {
Node res = keyNodeMap.get(key);
nodeList.moveNodeToTail(res);
return res.value;
}
return -1;
}
public void set(int key, int value) {
if (keyNodeMap.containsKey(key)) {
Node node = keyNodeMap.get(key);
node.value = value;
nodeList.moveNodeToTail(node);
} else {
Node newNode = new Node(key, value);
keyNodeMap.put(key, newNode);
nodeList.addNode(newNode);
if (keyNodeMap.size() == capacity + 1) {
removeMostUnusedCache();
}
}
}
private void removeMostUnusedCache() {
Node removeNode = nodeList.removeHead();
keyNodeMap.remove(removeNode.key);
}
}
}
}

@ -0,0 +1,67 @@
package 01_mca_04_about_data_structure;
import java.util.Stack;
public class Code06_AllTimesMinToMax {
public static int max1(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
int minNum = Integer.MAX_VALUE;
int sum = 0;
for (int k = i; k <= j; k++) {
sum += arr[k];
minNum = Math.min(minNum, arr[k]);
}
max = Math.max(max, minNum * sum);
}
}
return max;
}
public static int max2(int[] arr) {
int size = arr.length;
int[] sums = new int[size];
sums[0] = arr[0];
for (int i = 1; i < size; i++) {
sums[i] = sums[i - 1] + arr[i];
}
int max = Integer.MIN_VALUE;
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < size; i++) {
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
int j = stack.pop();
max = Math.max(max, (stack.isEmpty() ? sums[i - 1] : (sums[i - 1] - sums[stack.peek()])) * arr[j]);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int j = stack.pop();
max = Math.max(max, (stack.isEmpty() ? sums[size - 1] : (sums[size - 1] - sums[stack.peek()])) * arr[j]);
}
return max;
}
public static int[] gerenareRondomArray() {
int[] arr = new int[(int) (Math.random() * 20) + 10];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 101);
}
return arr;
}
public static void main(String[] args) {
int testTimes = 2000000;
System.out.println("test begin");
for (int i = 0; i < testTimes; i++) {
int[] arr = gerenareRondomArray();
if (max1(arr) != max2(arr)) {
System.out.println("FUCK!");
break;
}
}
System.out.println("test finish");
}
}

@ -0,0 +1,78 @@
package 01_mca_04_about_data_structure;
// 本题为leetcode原题
// 测试链接https://leetcode.com/problems/friend-circles/
// 可以直接通过
public class Code07_FriendCircles {
public static int findCircleNum(int[][] M) {
int N = M.length;
// {0} {1} {2} {N-1}
UnionFind unionFind = new UnionFind(N);
for (int i = 0; i < N; i++) {
for (int j = i + 1; j < N; j++) {
if (M[i][j] == 1) { // i和j互相认识
unionFind.union(i, j);
}
}
}
return unionFind.sets();
}
public static class UnionFind {
// parent[i] = k i的父亲是k
private int[] parent;
// size[i] = k 如果i是代表节点size[i]才有意义,否则无意义
// i所在的集合大小是多少
private int[] size;
// 辅助结构
private int[] help;
// 一共有多少个集合
private int sets;
public UnionFind(int N) {
parent = new int[N];
size = new int[N];
help = new int[N];
sets = N;
for (int i = 0; i < N; i++) {
parent[i] = i;
size[i] = 1;
}
}
// 从i开始一直往上往上到不能再往上代表节点返回
// 这个过程要做路径压缩
private int find(int i) {
int hi = 0;
while (i != parent[i]) {
help[hi++] = i;
i = parent[i];
}
for (hi--; hi >= 0; hi--) {
parent[help[hi]] = i;
}
return i;
}
public void union(int i, int j) {
int f1 = find(i);
int f2 = find(j);
if (f1 != f2) {
if (size[f1] >= size[f2]) {
size[f1] += size[f2];
parent[f2] = f1;
} else {
size[f2] += size[f1];
parent[f1] = f2;
}
sets--;
}
}
public int sets() {
return sets;
}
}
}

@ -0,0 +1,45 @@
package 01_mca_05_about_math_greedy;
public class Code01_AppleMinBags {
public static int minBags(int apple) {
if (apple < 0) {
return -1;
}
// apple >> 3 -> apple / 8
// 最多用几个8号袋
int bag8 = (apple >> 3);
// 最多的8号袋先试试剩余多少苹果
int rest = apple - (bag8 << 3);
while(bag8 >= 0) {
// rest 个
if(rest % 6 ==0) {
return bag8 + (rest / 6);
} else {
bag8--;
rest += 8;
}
}
return -1;
}
// O(1)
public static int minBagAwesome(int apple) {
if ((apple & 1) != 0) { // 如果是奇数,返回-1
return -1;
}
if (apple < 18) {
return apple == 0 ? 0 : (apple == 6 || apple == 8) ? 1
: (apple == 12 || apple == 14 || apple == 16) ? 2 : -1;
}
return (apple - 18) / 8 + 3;
}
public static void main(String[] args) {
for(int apple = 1; apple < 200;apple++) {
System.out.println(apple + " : "+ minBags(apple));
}
}
}

@ -0,0 +1,47 @@
package 01_mca_05_about_math_greedy;
public class Code02_MSumToN {
public static boolean isMSum1(int num) {
for (int start = 1; start <= num; start++) {
int sum = start;
for (int j = start + 1; j <= num; j++) {
if (sum + j > num) {
break;
}
if (sum + j == num) {
return true;
}
sum += j;
}
}
return false;
}
public static boolean isMSum2(int num) {
//
// return num == (num & (~num + 1)); // 是2的某次方不是连续和
//
//
// return !(num == (num & (~num + 1)));
//
// return num == (num & (-num));
//
//
return (num & (num - 1)) != 0;
}
public static void main(String[] args) {
for (int num = 1; num < 200; num++) {
System.out.println(num + " : " + isMSum1(num));
}
System.out.println("test begin");
for (int num = 1; num < 5000; num++) {
if (isMSum1(num) != isMSum2(num)) {
System.out.println("Oops!");
}
}
System.out.println("test end");
}
}

@ -0,0 +1,96 @@
package 01_mca_05_about_math_greedy;
public class Code03_EatGrass {
// 剩余的草是rest
// 对于process(rest)这个过程来说,当前的先手先拿!
// 返回值,是个字符串,"先" "后"
// "先" :当前的先手赢!
// "后" : 当前的先手输!
public static String process(int rest) {
if(rest < 5) {
return (rest == 0 || rest == 2) ? "后" : "先";
}
// rest >= 5;
// 当前的先手,可以开始选择了
// 1 4 16
for(int choice = 1; choice <= rest; choice *= 4) {
int next = rest - choice;
if(process(next).equals("后")) {
return "先";
}
}
return "后";
}
// 如果n份草最终先手赢返回"先手"
// 如果n份草最终后手赢返回"后手"
public static String whoWin(int n) {
if (n < 5) {
return n == 0 || n == 2 ? "后手" : "先手";
}
// 进到这个过程里来,当前的先手,先选
int want = 1;
while (want <= n) {
if (whoWin(n - want).equals("后手")) {
return "先手";
}
if (want <= (n / 4)) {
want *= 4;
} else {
break;
}
}
return "后手";
}
public static String winner1(int n) {
if (n < 5) {
return (n == 0 || n == 2) ? "后手" : "先手";
}
int base = 1;
while (base <= n) {
if (winner1(n - base).equals("后手")) {
return "先手";
}
if (base > n / 4) { // 防止base*4之后溢出
break;
}
base *= 4;
}
return "后手";
}
public static String winner2(int n) {
if (n % 5 == 0 || n % 5 == 2) {
return "后手";
} else {
return "先手";
}
}
public static void main(String[] args) {
for (int i = 0; i <= 50; i++) {
System.out.println(i + " : " + process(i));
}
}
}

@ -0,0 +1,111 @@
package 01_mca_05_about_math_greedy;
import java.util.Arrays;
import java.util.Comparator;
public class Code04_BestArrange {
public static class Program {
public int start;
public int end;
public Program(int start, int end) {
this.start = start;
this.end = end;
}
}
// 暴力!所有情况都尝试!
public static int bestArrange1(Program[] programs) {
if (programs == null || programs.length == 0) {
return 0;
}
return process(programs, 0, 0);
}
// 还剩下的会议都放在programs里
// done之前已经安排了多少会议的数量
// timeLine目前来到的时间点是什么
// 目前来到timeLine的时间点已经安排了done多的会议剩下的会议programs可以自由安排
// 返回能安排的最多会议数量
public static int process(Program[] programs, int done, int timeLine) {
if (programs.length == 0) {
return done;
}
// 还剩下会议
int max = done;
// 当前安排的会议是什么会,每一个都枚举
for (int i = 0; i < programs.length; i++) {
if (programs[i].start >= timeLine) {
Program[] next = copyButExcept(programs, i);
max = Math.max(max, process(next, done + 1, programs[i].end));
}
}
return max;
}
public static Program[] copyButExcept(Program[] programs, int i) {
Program[] ans = new Program[programs.length - 1];
int index = 0;
for (int k = 0; k < programs.length; k++) {
if (k != i) {
ans[index++] = programs[k];
}
}
return ans;
}
// 会议的开始时间和结束时间,都是数值,不会 < 0
public static int bestArrange2(Program[] programs) {
Arrays.sort(programs, new ProgramComparator());
int timeLine = 0;
int result = 0;
// 依次遍历每一个会议,结束时间早的会议先遍历
for (int i = 0; i < programs.length; i++) {
if (timeLine <= programs[i].start) {
result++;
timeLine = programs[i].end;
}
}
return result;
}
public static class ProgramComparator implements Comparator<Program> {
@Override
public int compare(Program o1, Program o2) {
return o1.end - o2.end;
}
}
// for test
public static Program[] generatePrograms(int programSize, int timeMax) {
Program[] ans = new Program[(int) (Math.random() * (programSize + 1))];
for (int i = 0; i < ans.length; i++) {
int r1 = (int) (Math.random() * (timeMax + 1));
int r2 = (int) (Math.random() * (timeMax + 1));
if (r1 == r2) {
ans[i] = new Program(r1, r1 + 1);
} else {
ans[i] = new Program(Math.min(r1, r2), Math.max(r1, r2));
}
}
return ans;
}
public static void main(String[] args) {
int programSize = 12;
int timeMax = 20;
int timeTimes = 1000000;
for (int i = 0; i < timeTimes; i++) {
Program[] programs = generatePrograms(programSize, timeMax);
if (bestArrange1(programs) != bestArrange2(programs)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}

@ -0,0 +1,35 @@
package 01_mca_05_about_math_greedy;
import java.util.Arrays;
import java.util.PriorityQueue;
// leetcode 630题
public class Code05_CourseScheduleIII {
public static int scheduleCourse(int[][] courses) {
// courses[i] = {花费,截止}
Arrays.sort(courses, (a, b) -> a[1] - b[1]);
// 花费时间的大根堆
PriorityQueue<Integer> heap = new PriorityQueue<>((a, b) -> b - a);
// 时间点
int time = 0;
for (int[] c : courses) {
//
if (time + c[0] <= c[1]) { // 当前时间 + 花费 <= 截止时间的
heap.add(c[0]);
time += c[0];
} else { // 当前时间 + 花费 > 截止时间的, 只有淘汰掉某课,当前的课才能进来!
//
if (!heap.isEmpty() && heap.peek() > c[0]) {
// time -= heap.poll();
// heap.add(c[0]);
// time += c[0];
heap.add(c[0]);
time += c[0] - heap.poll();
}
}
}
return heap.size();
}
}

@ -0,0 +1,162 @@
package 01_mca_05_about_math_greedy;
// 来自CMU入学申请考试
// 给定一个长度为 N 的字符串 S由字符'a'和'b'组成,空隙由 '?' 表示
// 你的任务是用a字符或b字符替换每个间隙
// 替换完成后想让连续出现同一种字符的最长子串尽可能短
// 例如S = "aa??bbb"
// 如果将"??"替换为"aa" ,即"aaaabbb"则由相等字符组成的最长子串长度为4
// 如果将"??"替换为"ba" ,即"aababbb"则由相等字符组成的最长子串长度为3
// 那么方案二是更好的结果返回3
// S的长度 <= 10^6
public class Code06_MinContinuousFragment {
// 暴力方法
// 为了验证
public static int minContinuous1(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
return process1(str, 0);
}
public static int process1(char[] str, int index) {
if (index == str.length) {
return maxLen(str);
} else {
if (str[index] != '?') {
return process1(str, index + 1);
} else {
str[index] = 'a';
int p1 = process1(str, index + 1);
str[index] = 'b';
int p2 = process1(str, index + 1);
str[index] = '?';
return Math.min(p1, p2);
}
}
}
// 正式方法
// 时间复杂度O(N)
public static int minContinuous2(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
int L = 0;
int R = -1;
for (int i = 0; i < N; i++) {
if (str[i] != '?') {
set(str, L, R);
L = i + 1;
R = i;
} else {
R++;
}
}
set(str, L, R);
// 下面的for循环是单独处理条件5
for (int i = 1; i < N; i++) {
if (str[i] == '?') {
// baaaa?bbbbbbbba
for (L = i - 1; L >= 0 && str[L] == str[i - 1]; L--)
;
for (R = i + 1; R < N && str[R] == str[i + 1]; R++)
;
L = i - L - 1;
R = R - i - 1;
if (L <= R) {
str[i] = str[i - 1];
} else {
str[i] = str[i + 1];
}
}
}
return maxLen(str);
}
// L...R 都是?
// 如果这一坨问号满足1234中的一种就填好
// 如果满足5就不填a?b
public static void set(char[] str, int L, int R) {
int N = str.length;
if (L > R) {
return;
}
if (L == 0 && R == N - 1) {
for (int i = 0; i < N; i++) {
str[i] = (i & 1) == 0 ? 'a' : 'b';
}
} else if (L == 0) {
for (int i = R; i >= 0; i--) {
str[i] = str[i + 1] == 'a' ? 'b' : 'a';
}
} else if (R == N - 1) {
for (int i = L; i < str.length; i++) {
str[i] = str[i - 1] == 'a' ? 'b' : 'a';
}
} else {
if (str[L - 1] == str[R + 1] || L != R) {
for (; L <= R; L++, R--) {
str[L] = str[L - 1] == 'a' ? 'b' : 'a';
str[R] = str[R + 1] == 'a' ? 'b' : 'a';
}
}
}
}
public static int maxLen(char[] str) {
int ans = 1;
int cur = 1;
for (int i = 1; i < str.length; i++) {
if (str[i] != str[i - 1]) {
ans = Math.max(ans, cur);
cur = 1;
} else {
cur++;
}
}
ans = Math.max(ans, cur);
return ans;
}
public static char[] arr = { 'a', 'b', '?' };
public static String randomString(int len) {
int N = (int) (Math.random() * (len + 1));
char[] str = new char[N];
for (int i = 0; i < N; i++) {
str[i] = arr[(int) (Math.random() * 3)];
}
return String.valueOf(str);
}
public static void main(String[] args) {
int len = 35;
int testTime = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
String s = randomString(len);
int ans1 = minContinuous1(s);
int ans2 = minContinuous2(s);
if (ans1 != ans2) {
System.out.println(s);
System.out.println(ans1);
System.out.println(ans2);
}
}
System.out.println("测试结束");
len = 10000000;
String s = randomString(len);
long start = System.currentTimeMillis();
minContinuous2(s);
long end = System.currentTimeMillis();
System.out.println("运行时间(毫秒):" + (end - start));
}
}

@ -0,0 +1,141 @@
package 01_mca_05_about_math_greedy;
import java.util.Arrays;
import java.util.HashMap;
// 来自小红书
// 有四种诗的韵律分别为: AABB、ABAB、ABBA、AAAA
// 比如 : 1 1 3 3就属于AABB型的韵律、6 6 6 6就属于AAAA型的韵律等等
// 一个数组arr当然可以生成很多的子序列如果某个子序列一直以韵律的方式连接起来我们称这样的子序列是有效的
// 比如, arr = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 }
// arr的一个子序列为{1, 1, 1, 1, 2, 3, 3, 2, 4, 3, 4, 3, 7, 7, 9, 9}
// 其中1, 1, 1, 1是AAAA、2, 3, 3, 2是ABBA、4, 3, 4, 3是ABAB、7, 7, 9, 9是AABB
// 可以看到,整个子序列一直以韵律的方式连接起来,所以这个子序列是有效的
// 给定一个数组arr, 返回最长的有效子序列长度
// 题目限制 : arr长度 <= 4000, arr中的值<= 10^9
// 离散化之后arr长度 <= 4000, arr中的值<= 4000
public class Code07_PoemProblem {
// AABB
// ABAB
// ABBA
// AAAA
public static int maxLen1(int[] arr) {
if (arr == null || arr.length < 4) {
return 0;
}
int[] path = new int[arr.length];
return process1(arr, 0, path, 0);
}
public static int process1(int[] arr, int index, int[] path, int size) {
if (index == arr.length) {
if (size % 4 != 0) {
return 0;
} else {
for (int i = 0; i < size; i += 4) {
if (!valid(path, i)) {
return 0;
}
}
return size;
}
} else {
int p1 = process1(arr, index + 1, path, size);
path[size] = arr[index];
int p2 = process1(arr, index + 1, path, size + 1);
return Math.max(p1, p2);
}
}
public static boolean valid(int[] p, int i) {
return (p[i] == p[i + 1] && p[i + 2] == p[i + 3])
|| (p[i] == p[i + 2] && p[i + 1] == p[i + 3] && p[i] != p[i + 1])
|| (p[i] == p[i + 3] && p[i + 1] == p[i + 2] && p[i] != p[i + 1]);
}
// 课堂有同学提出了贪心策略(这题还真是有贪心策略),是正确的
// AABB
// ABAB
// ABBA
// AAAA
// 先看前三个规则AABB、ABAB、ABBA
// 首先A、A、B、B的全排列为:
// AABB -> AABB
// ABAB -> ABAB
// ABBA -> ABBA
// BBAA -> 等同于AABB因为A和B谁在前、谁在后都算是 : AABB的范式
// BABA -> 等同于ABAB因为A和B谁在前、谁在后都算是 : ABAB的范式
// BAAB -> 等同于ABBA因为A和B谁在前、谁在后都算是 : ABBA的范式
// 也就是说AABB、ABAB、ABBA这三个规则可以这么用
// 只要有两个不同的数都出现2次那么这一共4个数就一定符合韵律规则。
// 所以:
// 1) 当来到arr中的一个数字num的时候
// 如果num已经出现了2次了, 只要之前还有一个和num不同的数
// 也出现了两次,则一定符合了某个规则, 长度直接+4然后清空所有的统计
// 2) 当来到arr中的一个数字num的时候,
// 如果num已经出现了4次了(规则四), 长度直接+4然后清空所有的统计
// 但是如果我去掉某个规则,该贪心直接报废,比如韵律规则变成:
// AABB、ABAB、AAAA
// 因为少了ABBA, 所以上面的化简不成立了, 得重新分析新规则下的贪心策略
// 而尝试的方法就更通用(也就是maxLen3),只是减少一个分支而已
// 这个贪心费了很多心思,值得点赞!
public static int maxLen2(int[] arr) {
// 统计某个数(key),出现的次数(value)
HashMap<Integer, Integer> map = new HashMap<>();
// tow代表目前有多少数出现了2次
int two = 0;
// ans代表目前符合韵律链接的子序列增长到了多长
int ans = 0;
// 当前的num出现了几次
int numTimes = 0;
for (int num : arr) {
// 对当前的num做次数统计
map.put(num, map.getOrDefault(num, 0) + 1);
// 把num出现的次数拿出来
numTimes = map.get(num);
// 如果num刚刚出现了2次, 那么目前出现了2次的数的数量需要增加1个
two += numTimes == 2 ? 1 : 0;
// 下面的if代表 :
// 如果目前有2个数出现2次了可以连接了
// 如果目前有1个数出现4次了可以连接了
if (two == 2 || numTimes == 4) {
ans += 4;
map.clear();
two = 0;
}
}
return ans;
}
// 为了测试
public static int[] randomArray(int len, int value) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * value);
}
return arr;
}
// 为了测试
public static void main(String[] args) {
// 1111 2332 4343 7799
int[] test = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 };
System.out.println(maxLen1(test));
System.out.println(maxLen2(test));
System.out.println("===========");
int len = 16;
int value = 10;
int[] arr = randomArray(len, value);
int[] arr1 = Arrays.copyOf(arr, arr.length);
int[] arr2 = Arrays.copyOf(arr, arr.length);
System.out.println(maxLen1(arr1));
System.out.println(maxLen2(arr2));
System.out.println("===========");
}
}

@ -0,0 +1,51 @@
本节并无code因为资源限制类题目输入需要的条件较多并且真的实现代码量巨大
面试中这类题目出现也就和面试官聊解法,不会有代码实现的要求
题目1
32位无符号整数的范围是0~4,294,967,295
现在有一个正好包含40亿个无符号整数的文件
所以在整个范围中必然存在没出现过的数,
可以使用最多1GB的内存怎么找到所有未出现过的数
进阶:
内存限制为 3KB但是只用找到一个没出现过的数即可
题目2
32位无符号整数的范围是0~4294967295
现在有40亿个无符号整数可以使用最多1GB的内存
找出所有出现了两次的数
题目3
32位无符号整数的范围是0~4294967295
现在有40亿个无符号整数
可以使用最多3K的内存怎么找到这40亿个整数的中位数
题目4
32位无符号整数的范围是0~4294967295
有一个10G大小的文件每一行都装着这种类型的数字
整个文件是无序的给你5G的内存空间
请你输出一个10G大小的文件就是原文件所有数字排序的结果
题目5
设计一个超级uuid系统
题目6
给定一个非常大的List<String> list
每一个字符串类似 : "hello,world,have,hello,world"
这一个字符串中有2个hello2个world1个have
请设计一种多线程处理方案统计list中每一个字符串
切分出来的单词数量,并且汇总
最终返回一个HashMap<String, Integer>表示每个字符串出现几次

@ -0,0 +1,87 @@
package 01_mca_07_about_monotonicity;
import java.util.Arrays;
public class Code01_CordCoverMaxPoint {
public static int maxPoint1(int[] arr, int L) {
int res = 1;
for (int i = 0; i < arr.length; i++) {
int nearest = nearestIndex(arr, i, arr[i] - L);
res = Math.max(res, i - nearest + 1);
}
return res;
}
public static int nearestIndex(int[] arr, int R, int value) {
int L = 0;
int index = R;
while (L <= R) {
int mid = L + ((R - L) >> 1);
if (arr[mid] >= value) {
index = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
return index;
}
public static int maxPoint2(int[] arr, int L) {
int left = 0;
int right = 0;
int N = arr.length;
int max = 0;
while (left < N) {
while (right < N && arr[right] - arr[left] <= L) {
right++;
}
max = Math.max(max, right - (left++));
}
return max;
}
// for test
public static int test(int[] arr, int L) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
int pre = i - 1;
while (pre >= 0 && arr[i] - arr[pre] <= L) {
pre--;
}
max = Math.max(max, i - pre);
}
return max;
}
// for test
public static int[] generateArray(int len, int max) {
int[] ans = new int[(int) (Math.random() * len) + 1];
for (int i = 0; i < ans.length; i++) {
ans[i] = (int) (Math.random() * max);
}
Arrays.sort(ans);
return ans;
}
public static void main(String[] args) {
int len = 100;
int max = 1000;
int testTime = 100000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int L = (int) (Math.random() * max);
int[] arr = generateArray(len, max);
int ans1 = maxPoint1(arr, L);
int ans2 = maxPoint2(arr, L);
int ans3 = test(arr, L);
if (ans1 != ans2 || ans2 != ans3) {
System.out.println("oops!");
break;
}
}
}
}

@ -0,0 +1,91 @@
package 01_mca_07_about_monotonicity;
public class Code02_LongestSumSubArrayLengthInPositiveArray {
public static int getMaxLength(int[] arr, int K) {
if (arr == null || arr.length == 0 || K <= 0) {
return 0;
}
int left = 0;
int right = 0;
int sum = arr[0];
int len = 0;
while (right < arr.length) {
if (sum == K) {
len = Math.max(len, right - left + 1);
sum -= arr[left++];
} else if (sum < K) {
right++;
if (right == arr.length) {
break;
}
sum += arr[right];
} else {
sum -= arr[left++];
}
}
return len;
}
// for test
public static int right(int[] arr, int K) {
int max = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
if (valid(arr, i, j, K)) {
max = Math.max(max, j - i + 1);
}
}
}
return max;
}
// for test
public static boolean valid(int[] arr, int L, int R, int K) {
int sum = 0;
for (int i = L; i <= R; i++) {
sum += arr[i];
}
return sum == K;
}
// for test
public static int[] generatePositiveArray(int size, int value) {
int[] ans = new int[size];
for (int i = 0; i != size; i++) {
ans[i] = (int) (Math.random() * value) + 1;
}
return ans;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int len = 50;
int value = 100;
int testTime = 500000;
System.out.println("test begin");
for (int i = 0; i < testTime; i++) {
int[] arr = generatePositiveArray(len, value);
int K = (int) (Math.random() * value) + 1;
int ans1 = getMaxLength(arr, K);
int ans2 = right(arr, K);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println("K : " + K);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("test end");
}
}

@ -0,0 +1,113 @@
package 01_mca_07_about_monotonicity;
import java.util.Arrays;
public class Code03_Heaters {
// 比如地点是7, 9, 14
// 供暖点的位置: 1 3 4 5 13 15 17
// 先看地点7
// 由1供暖半径是6
// 由3供暖半径是4
// 由4供暖半径是3
// 由5供暖半径是2
// 由13供暖半径是6
// 由此可知地点7应该由供暖点5来供暖半径是2
// 再看地点9
// 供暖点不回退
// 由5供暖半径是4
// 由13供暖半径是4
// 由15供暖半径是6
// 由此可知地点9应该由供暖点13来供暖半径是4
// 为什么是13而不是5因为接下来的地点都会更靠右所以半径一样的时候就应该选更右的供暖点
// 再看地点14
// 供暖点不回退
// 由13供暖半径是1
// 由15供暖半径是1
// 由17供暖半径是3
// 由此可知地点14应该由供暖点15来供暖半径是1
// 以此类推
public static int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int ans = 0;
// 时间复杂度O(N)
// i是地点j是供暖点
for (int i = 0, j = 0; i < houses.length; i++) {
while (!best(houses, heaters, i, j)) {
j++;
}
ans = Math.max(ans, Math.abs(heaters[j] - houses[i]));
}
return ans;
}
// 这个函数含义:
// 当前的地点houses[i]由heaters[j]来供暖是最优的吗?
// 当前的地点houses[i]由heaters[j]来供暖产生的半径是a
// 当前的地点houses[i]由heaters[j + 1]来供暖产生的半径是b
// 如果a < b, 说明是最优,供暖不应该跳下一个位置
// 如果a >= b, 说明不是最优,应该跳下一个位置
public static boolean best(int[] houses, int[] heaters, int i, int j) {
return j == heaters.length - 1
|| Math.abs(heaters[j] - houses[i]) < Math.abs(heaters[j + 1] - houses[i]);
}
// 下面这个方法不对,你能找出原因嘛?^_^
public static int findRadius2(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int ans = 0;
// 时间复杂度O(N)
// i是地点j是供暖点
for (int i = 0, j = 0; i < houses.length; i++) {
while (!best2(houses, heaters, i, j)) {
j++;
}
ans = Math.max(ans, Math.abs(heaters[j] - houses[i]));
}
return ans;
}
public static boolean best2(int[] houses, int[] heaters, int i, int j) {
return j == heaters.length - 1 || Math.abs(heaters[j] - houses[i]) <= Math.abs(heaters[j + 1] - houses[i]);
}
// 为了测试
public static int[] randomArray(int len, int v) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * v) + 1;
}
return arr;
}
// 为了测试
public static void main(String[] args) {
int len = 5;
int v = 10;
int testTime = 10000;
for (int i = 0; i < testTime; i++) {
int[] a = randomArray(len, v);
int[] b = randomArray(len, v);
int ans1 = findRadius(a, b);
int ans2 = findRadius2(a, b);
if (ans1 != ans2) {
System.out.println("A : ");
for (int num : a) {
System.out.print(num + " ");
}
System.out.println();
System.out.println("B : ");
for (int num : b) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
}
}

@ -0,0 +1,28 @@
package 01_mca_07_about_monotonicity;
// 本题测试链接 : https://leetcode.com/problems/trapping-rain-water/
public class Code04_TrappingRainWater {
public static int trap(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
int N = arr.length;
int L = 1;
int leftMax = arr[0];
int R = N - 2;
int rightMax = arr[N - 1];
int water = 0;
while (L <= R) {
if (leftMax <= rightMax) {
water += Math.max(0, leftMax - arr[L]);
leftMax = Math.max(leftMax, arr[L++]);
} else {
water += Math.max(0, rightMax - arr[R]);
rightMax = Math.max(rightMax, arr[R--]);
}
}
return water;
}
}

@ -0,0 +1,75 @@
package 01_mca_07_about_monotonicity;
import java.util.Arrays;
public class Code05_BSNearLeft {
// 在arr上找满足>=value的最左位置
public static int nearestIndex(int[] arr, int value) {
int L = 0;
int R = arr.length - 1;
int index = -1; // 记录最左的对号
while (L <= R) { // 至少一个数的时候
int mid = L + ((R - L) >> 1);
if (arr[mid] >= value) {
index = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
return index;
}
// for test
public static int test(int[] arr, int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] >= value) {
return i;
}
}
return -1;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 10;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
Arrays.sort(arr);
int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
if (test(arr, value) != nearestIndex(arr, value)) {
printArray(arr);
System.out.println(value);
System.out.println(test(arr, value));
System.out.println(nearestIndex(arr, value));
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}

@ -0,0 +1,58 @@
package 01_mca_07_about_monotonicity;
import java.util.HashMap;
public class Code06_IsStepSum {
public static boolean isStepSum(int stepSum) {
int L = 0;
int R = stepSum;
int M = 0;
int cur = 0;
while (L <= R) {
M = L + ((R - L) >> 1);
cur = stepSum(M);
if (cur == stepSum) {
return true;
} else if (cur < stepSum) {
L = M + 1;
} else {
R = M - 1;
}
}
return false;
}
public static int stepSum(int num) {
int sum = 0;
while (num != 0) {
sum += num;
num /= 10;
}
return sum;
}
// for test
public static HashMap<Integer, Integer> generateStepSumNumberMap(int numMax) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i <= numMax; i++) {
map.put(stepSum(i), i);
}
return map;
}
// for test
public static void main(String[] args) {
int max = 1000000;
int maxStepSum = stepSum(max);
HashMap<Integer, Integer> ans = generateStepSumNumberMap(max);
System.out.println("测试开始");
for (int i = 0; i <= maxStepSum; i++) {
if (isStepSum(i) ^ ans.containsKey(i)) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,114 @@
package 01_mca_07_about_monotonicity;
public class Code07_MinWindowLength {
// 字符串都是小写字母
public static int validMinLen(String s1, String s2) {
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int n = str1.length;
int m = str2.length;
if (n < m) {
return -1;
}
// n >= m
int[] counts = new int[256];
for (char s2char : str2) {
counts[s2char]++;
}
int all = m;
int ans = Integer.MAX_VALUE;
int r = 0;
// [i..r)
for (int i = 0; i < n; i++) {
r = Math.max(r, i);
while (r < n && all > 0) {
counts[str1[r]]--;
if (counts[str1[r]] >= 0) {
all--;
}
r++;
}
if (all == 0) {
ans = Math.min(ans, r - i);
}
counts[str1[i]]++;
if (counts[str1[i]] > 0) {
all++;
}
}
return ans == Integer.MAX_VALUE ? -1 : ans;
}
public static int minLength(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() < s2.length()) {
return Integer.MAX_VALUE;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int[] map = new int[256]; // map[37] = 4 37 4次
for (int i = 0; i != str2.length; i++) {
map[str2[i]]++;
}
int all = str2.length;
int L = 0;
int R = 0;
int minLen = Integer.MAX_VALUE;
while (R != str1.length) {
map[str1[R]]--;
if (map[str1[R]] >= 0) {
all--;
}
if (all == 0) {
while (map[str1[L]] < 0) {
map[str1[L++]]++;
}
minLen = Math.min(minLen, R - L + 1);
all++;
map[str1[L++]]++;
}
R++;
}
return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
// 测试链接 : https://leetcode.com/problems/minimum-window-substring/
public static String minWindow(String s, String t) {
if (s.length() < t.length()) {
return "";
}
char[] str = s.toCharArray();
char[] target = t.toCharArray();
int[] map = new int[256];
for (char cha : target) {
map[cha]++;
}
int all = target.length;
int L = 0;
int R = 0;
int minLen = Integer.MAX_VALUE;
int ansl = -1;
int ansr = -1;
while (R != str.length) {
map[str[R]]--;
if (map[str[R]] >= 0) {
all--;
}
if (all == 0) {
while (map[str[L]] < 0) {
map[str[L++]]++;
}
if (minLen > R - L + 1) {
minLen = R - L + 1;
ansl = L;
ansr = R;
}
all++;
map[str[L++]]++;
}
R++;
}
return minLen == Integer.MAX_VALUE ? "" : s.substring(ansl, ansr + 1);
}
}

@ -0,0 +1,49 @@
package 01_mca_07_about_monotonicity;
// leetcode原题
// 测试链接https://leetcode.com/problems/split-array-largest-sum/
public class Code08_SplitArrayLargestSum {
public static int splitArray3(int[] nums, int M) {
long sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
// 0 ~ sum
long l = 0;
long r = sum;
long ans = 0;
while (l <= r) {
long limit = (l + r) / 2;
long cur = f(nums, limit);
if (cur <= M) {
ans = limit;
r = limit - 1;
} else {
l = limit + 1;
}
}
return (int) ans;
}
public static int f(int[] arr, long limit) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] > limit) {
return Integer.MAX_VALUE;
}
}
int parts = 1;
int all = arr[0];
for (int i = 1; i < arr.length; i++) {
if (all + arr[i] > limit) {
parts++;
all = arr[i];
} else {
all += arr[i];
}
}
return parts;
}
}

@ -0,0 +1,165 @@
package 01_mca_07_about_monotonicity;
import java.util.List;
import java.util.ArrayList;
import java.util.Stack;
public class Code09_MonotonousStack {
// arr = [ 3, 1, 2, 3]
// 0 1 2 3
// [
// 0 : [-1, 1]
// 1 : [-1, -1]
// 2 : [ 1, -1]
// 3 : [ 2, -1]
// ]
public static int[][] getNearLessNoRepeat(int[] arr) {
int[][] res = new int[arr.length][2];
// 只存位置!
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < arr.length; i++) { // 当遍历到i位置的数arr[i]
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
int j = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
res[j][0] = leftLessIndex;
res[j][1] = i;
}
stack.push(i);
}
while (!stack.isEmpty()) {
int j = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
res[j][0] = leftLessIndex;
res[j][1] = -1;
}
return res;
}
public static int[][] getNearLess(int[] arr) {
int[][] res = new int[arr.length][2];
Stack<List<Integer>> stack = new Stack<>();
for (int i = 0; i < arr.length; i++) { // i -> arr[i] 进栈
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() - 1);
for (Integer popi : popIs) {
res[popi][0] = leftLessIndex;
res[popi][1] = -1;
}
}
return res;
}
// for test
public static int[] getRandomArrayNoRepeat(int size) {
int[] arr = new int[(int) (Math.random() * size) + 1];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
for (int i = 0; i < arr.length; i++) {
int swapIndex = (int) (Math.random() * arr.length);
int tmp = arr[swapIndex];
arr[swapIndex] = arr[i];
arr[i] = tmp;
}
return arr;
}
// for test
public static int[] getRandomArray(int size, int max) {
int[] arr = new int[(int) (Math.random() * size) + 1];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * max) - (int) (Math.random() * max);
}
return arr;
}
// for test
public static int[][] rightWay(int[] arr) {
int[][] res = new int[arr.length][2];
for (int i = 0; i < arr.length; i++) {
int leftLessIndex = -1;
int rightLessIndex = -1;
int cur = i - 1;
while (cur >= 0) {
if (arr[cur] < arr[i]) {
leftLessIndex = cur;
break;
}
cur--;
}
cur = i + 1;
while (cur < arr.length) {
if (arr[cur] < arr[i]) {
rightLessIndex = cur;
break;
}
cur++;
}
res[i][0] = leftLessIndex;
res[i][1] = rightLessIndex;
}
return res;
}
// for test
public static boolean isEqual(int[][] res1, int[][] res2) {
if (res1.length != res2.length) {
return false;
}
for (int i = 0; i < res1.length; i++) {
if (res1[i][0] != res2[i][0] || res1[i][1] != res2[i][1]) {
return false;
}
}
return true;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int size = 10;
int max = 20;
int testTimes = 2000000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int[] arr1 = getRandomArrayNoRepeat(size);
int[] arr2 = getRandomArray(size, max);
if (!isEqual(getNearLessNoRepeat(arr1), rightWay(arr1))) {
System.out.println("Oops!");
printArray(arr1);
break;
}
if (!isEqual(getNearLess(arr2), rightWay(arr2))) {
System.out.println("Oops!");
printArray(arr2);
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,58 @@
package 01_mca_07_about_monotonicity;
import java.util.Stack;
// 测试链接https://leetcode.com/problems/largest-rectangle-in-histogram
public class Code10_LargestRectangleInHistogram {
public static int largestRectangleArea1(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
int curArea = (height.length - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
public static int largestRectangleArea2(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int N = height.length;
int[] stack = new int[N];
int si = -1;
int maxArea = 0;
for (int i = 0; i < height.length; i++) {
while (si != -1 && height[i] <= height[stack[si]]) {
int j = stack[si--];
int k = si == -1 ? -1 : stack[si];
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
stack[++si] = i;
}
while (si != -1) {
int j = stack[si--];
int k = si == -1 ? -1 : stack[si];
int curArea = (height.length - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
}

@ -0,0 +1,158 @@
package 01_mca_08_dp;
public class Code01_Knapsack {
// weight和value数组等长
public static int maxValue1(int[] weight, int[] value, int bagLimit) {
return f1(weight, value, 0, bagLimit);
}
// 0....n-1
// 0...index-1 已经不能选了!
// index...n-1这些货自由挑选
// 背包还剩下多少容量rest自由挑选是不能超过rest的
// index...n-1在符合要求的情况下最大价值能达到多少
public static int f1(int[] weight, int[] value, int index, int rest) {
if (rest < 0) { // 剩余的负重是负数,说明之前的选择是错误的!
return -1; // 无效解!
}
// rest >= 0
if (index == weight.length) { // 没货了!
return 0;
}
// 既有负重,又有货物
// 第一种选择当前index位置的货没要
int p1 = f1(weight, value, index + 1, rest);
// 第二种选择当前index位置的货
int p2 = -1;
int next = f1(weight, value, index + 1, rest - weight[index]);
if (next != -1) {
p2 = value[index] + next;
}
return Math.max(p1, p2);
}
// weight和value数组等长
public static int maxValue2(int[] weight, int[] value, int bagLimit) {
int n = weight.length;
// index: 0...n
// bag : 100 0..100
int[][] dp = new int[n+1][bagLimit+1];
for(int i = 0; i<= n;i++) {
for(int j = 0 ; j <= bagLimit;j++) {
dp[i][j] = -2;
}
}
return f2(weight, value, 0, bagLimit, dp);
}
public static int f2(int[] weight, int[] value, int index, int rest, int[][] dp) {
if (rest < 0) {
return -1;
}
if(dp[index][rest] != -2) { // 之前算过!
return dp[index][rest];
}
// 缓存没命中!
int ans = 0;
if(index == weight.length) {
ans = 0;
} else {
int p1 = f2(weight, value, index + 1, rest, dp);
int p2 = -1;
int next = f2(weight, value, index + 1, rest - weight[index], dp);
if (next != -1) {
p2 = value[index] + next;
}
ans = Math.max(p1, p2);
}
dp[index][rest] = ans;
return ans;
}
// 所有的货重量和价值都在w和v数组里
// 为了方便,其中没有负数
// bag背包容量不能超过这个载重
// 返回:不超重的情况下,能够得到的最大价值
// public static int maxValue(int[] w, int[] v, int bag) {
// if (w == null || v == null || w.length != v.length || w.length == 0) {
// return 0;
// }
// // 尝试函数!
// return process(w, v, 0, bag);
// }
//
// // index 0~N
// // rest 负~bag
// public static int process(int[] w, int[] v, int index, int rest) {
// if (rest < 0) {
// return -1;
// }
// if (index == w.length) {
// return 0;
// }
// int p1 = process(w, v, index + 1, rest);
// int p2 = 0;
// int next = process(w, v, index + 1, rest - w[index]);
// if (next != -1) {
// p2 = v[index] + next;
// }
// return Math.max(p1, p2);
// }
//
// public static int dp(int[] w, int[] v, int bag) {
// if (w == null || v == null || w.length != v.length || w.length == 0) {
// return 0;
// }
// int N = w.length;
// int[][] dp = new int[N + 1][bag + 1];
// for (int index = N - 1; index >= 0; index--) {
// for (int rest = 0; rest <= bag; rest++) {
// int p1 = dp[index + 1][rest];
// int p2 = 0;
// int next = rest - w[index] < 0 ? -1 : dp[index + 1][rest - w[index]];
// if (next != -1) {
// p2 = v[index] + next;
// }
// dp[index][rest] = Math.max(p1, p2);
// }
// }
// return dp[0][bag];
// }
public static int[] randomArray(int len, int value) {
int[] arr = new int[len];
for(int i = 0 ; i< len;i++) {
arr[i] = (int)(Math.random() * value) + 1;
}
return arr;
}
public static void main(String[] args) {
int len = 30;
int value = 50;
int[] weight = randomArray(len, value);
int[] values = randomArray(len, value );
int bag = 2000;
long start;
long end;
// 2 ^ 30
start = System.currentTimeMillis();
System.out.println(maxValue1(weight, values, bag));
end = System.currentTimeMillis();
System.out.println("运行时间(毫秒):" + (end - start));
// 30 * 2000 -> 60000
start = System.currentTimeMillis();
System.out.println(maxValue2(weight, values, bag));
end = System.currentTimeMillis();
System.out.println("运行时间(毫秒):" + (end - start));
}
}

@ -0,0 +1,27 @@
package 01_mca_08_dp;
public class Code02_SumWays {
// arr中都是正数sum也是正数
// 求组成sum的方法数量
public static int sumWays(int[] arr, int sum) {
int n = arr.length;
if (n == 0) {
return sum == 0 ? 1 : 0;
}
int[][] dp = new int[n][sum + 1];
for (int i = 0; i < n; i++) {
dp[i][0] = 1;
}
if (arr[0] <= sum) {
dp[0][arr[0]] = 1;
}
for (int i = 1; i < n; i++) {
for (int j = 1; j <= sum; j++) {
dp[i][j] = dp[i - 1][j] + (j - arr[i] >= 0 ? dp[i - 1][j - arr[i]] : 0);
}
}
return dp[n - 1][sum];
}
}

@ -0,0 +1,27 @@
package 01_mca_08_dp;
// 本题测试链接 : https://leetcode.com/problems/longest-substring-without-repeating-characters/
public class Code03_LongestSubstringWithoutRepeatingCharacters {
public static int lengthOfLongestSubstring(String s) {
if (s == null || s.equals("")) {
return 0;
}
char[] str = s.toCharArray();
int[] map = new int[256];
for (int i = 0; i < 256; i++) {
map[i] = -1;
}
map[str[0]] = 0;
int N = str.length;
int ans = 1;
int pre = 1;
for (int i = 1; i < N; i++) {
pre = Math.min(i - map[str[i]], pre + 1);
ans = Math.max(ans, pre);
map[str[i]] = i;
}
return ans;
}
}

@ -0,0 +1,35 @@
package 01_mca_08_dp;
// 本题测试链接 : https://leetcode.com/problems/maximum-subarray/
public class Code04_SubArrayMaxSum {
public static int maxSubArray(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int max = Integer.MIN_VALUE;
int cur = 0;
for (int i = 0; i < arr.length; i++) {
cur += arr[i];
max = Math.max(max, cur);
cur = cur < 0 ? 0 : cur;
}
return max;
}
public static int maxSubArray2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
// 上一步dp的值
// dp[0]
int pre = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
pre = Math.max(arr[i], arr[i] + pre);
max = Math.max(max, pre);
}
return max;
}
}

@ -0,0 +1,208 @@
package 01_mca_08_dp;
public class Code05_CardsInLine {
public static int getWinnerScore(int[] arr) {
int xian = xian(arr, 0, arr.length - 1);
int hou = hou(arr, 0, arr.length - 1);
return Math.max(xian, hou);
}
// 目前是在arr[L...R]这个范围上玩牌!
// 返回,先手最终的最大得分
public static int xian(int[] arr, int L, int R) {
if (L == R) {
return arr[L];
}
if (L == R - 1) {
return Math.max(arr[L], arr[R]);
}
// L...R上不止两张牌
// 可能性1拿走L位置的牌
int p1 = arr[L] + hou(arr, L + 1, R);
// 可能性2拿走R位置的牌
int p2 = arr[R] + hou(arr, L, R - 1);
return Math.max(p1, p2);
}
// 目前是在arr[L...R]这个范围上玩牌!
// 返回,后手最终的最大得分
public static int hou(int[] arr, int L, int R) {
if (L == R) {
return 0;
}
if (L == R - 1) {
return Math.min(arr[L], arr[R]);
}
// L..R上不止两张牌
// 后手!
// 可能性1对手先手拿走L位置的牌接下来你就可以在L+1..R上先手了
int p1 = xian(arr, L + 1, R);
// 可能性2对手先手拿走R位置的牌接下来你就可以在L..R-1上先手了
int p2 = xian(arr, L, R - 1);
return Math.min(p1, p2);
}
public static int getWinnerScore2(int[] arr) {
int n = arr.length;
int[][] dpxian = new int[n][n];
int[][] dphou = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dpxian[i][j] = -1;
dphou[i][j] = -1;
}
}
int xian = xian2(arr, 0, arr.length - 1, dpxian, dphou);
int hou = hou2(arr, 0, arr.length - 1, dpxian, dphou);
return Math.max(xian, hou);
}
public static int xian2(int[] arr, int L, int R, int[][] dpxian, int[][] dphou) {
if (dpxian[L][R] != -1) {
return dpxian[L][R];
}
int ans = 0;
if (L == R) {
ans = arr[L];
} else if (L == R - 1) {
ans = Math.max(arr[L], arr[R]);
} else {
int p1 = arr[L] + hou2(arr, L + 1, R, dpxian, dphou);
int p2 = arr[R] + hou2(arr, L, R - 1, dpxian, dphou);
ans = Math.max(p1, p2);
}
dpxian[L][R] = ans;
return ans;
}
public static int hou2(int[] arr, int L, int R, int[][] dpxian, int[][] dphou) {
if (dphou[L][R] != -1) {
return dphou[L][R];
}
int ans = 0;
if (L == R) {
ans = 0;
} else if (L == R - 1) {
ans = Math.min(arr[L], arr[R]);
} else {
int p1 = xian2(arr, L + 1, R, dpxian, dphou);
int p2 = xian2(arr, L, R - 1, dpxian, dphou);
ans = Math.min(p1, p2);
}
dphou[L][R] = ans;
return ans;
}
// // 根据规则,返回获胜者的分数
// public static int win1(int[] arr) {
// if (arr == null || arr.length == 0) {
// return 0;
// }
// int first = f1(arr, 0, arr.length - 1);
// int second = g1(arr, 0, arr.length - 1);
// return Math.max(first, second);
// }
//
// // arr[L..R],先手获得的最好分数返回
// public static int f1(int[] arr, int L, int R) {
// if (L == R) {
// return arr[L];
// }
// int p1 = arr[L] + g1(arr, L + 1, R);
// int p2 = arr[R] + g1(arr, L, R - 1);
// return Math.max(p1, p2);
// }
//
// // // arr[L..R],后手获得的最好分数返回
// public static int g1(int[] arr, int L, int R) {
// if (L == R) {
// return 0;
// }
// int p1 = f1(arr, L + 1, R); // 对手拿走了L位置的数
// int p2 = f1(arr, L, R - 1); // 对手拿走了R位置的数
// return Math.min(p1, p2);
// }
//
// public static int win2(int[] arr) {
// if (arr == null || arr.length == 0) {
// return 0;
// }
// int N = arr.length;
// int[][] fmap = new int[N][N];
// int[][] gmap = new int[N][N];
// for (int i = 0; i < N; i++) {
// for (int j = 0; j < N; j++) {
// fmap[i][j] = -1;
// gmap[i][j] = -1;
// }
// }
// int first = f2(arr, 0, arr.length - 1, fmap, gmap);
// int second = g2(arr, 0, arr.length - 1, fmap, gmap);
// return Math.max(first, second);
// }
//
// // arr[L..R],先手获得的最好分数返回
// public static int f2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {
// if (fmap[L][R] != -1) {
// return fmap[L][R];
// }
// int ans = 0;
// if (L == R) {
// ans = arr[L];
// } else {
// int p1 = arr[L] + g2(arr, L + 1, R, fmap, gmap);
// int p2 = arr[R] + g2(arr, L, R - 1, fmap, gmap);
// ans = Math.max(p1, p2);
// }
// fmap[L][R] = ans;
// return ans;
// }
//
// // // arr[L..R],后手获得的最好分数返回
// public static int g2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {
// if (gmap[L][R] != -1) {
// return gmap[L][R];
// }
// int ans = 0;
// if (L != R) {
// int p1 = f2(arr, L + 1, R, fmap, gmap); // 对手拿走了L位置的数
// int p2 = f2(arr, L, R - 1, fmap, gmap); // 对手拿走了R位置的数
// ans = Math.min(p1, p2);
// }
// gmap[L][R] = ans;
// return ans;
// }
//
// public static int win3(int[] arr) {
// if (arr == null || arr.length == 0) {
// return 0;
// }
// int N = arr.length;
// int[][] fmap = new int[N][N];
// int[][] gmap = new int[N][N];
// for (int i = 0; i < N; i++) {
// fmap[i][i] = arr[i];
// }
// for (int startCol = 1; startCol < N; startCol++) {
// int L = 0;
// int R = startCol;
// while (R < N) {
// fmap[L][R] = Math.max(arr[L] + gmap[L + 1][R], arr[R] + gmap[L][R - 1]);
// gmap[L][R] = Math.min(fmap[L + 1][R], fmap[L][R - 1]);
// L++;
// R++;
// }
// }
// return Math.max(fmap[0][N - 1], gmap[0][N - 1]);
// }
public static void main(String[] args) {
int[] arr = { 5, 7, 4, 5, 8, 1, 6, 0, 3, 4, 6, 1, 7 };
System.out.println(getWinnerScore(arr));
System.out.println(getWinnerScore2(arr));
}
}

@ -0,0 +1,180 @@
package 01_mca_08_dp;
// 本题测试链接 : https://leetcode.com/problems/burst-balloons/
public class Code06_BurstBalloons {
public static int maxScore1(int[] arr) {
int n = arr.length;
int[] help = new int[n + 2];
help[0] = 1;
help[n + 1] = 1;
for (int i = 1; i <= n; i++) {
help[i] = arr[i - 1];
}
// [3,2,1,4,5] -> [1,3,2,1,4,5,1];
// 0 1 2 3 4 1 2 3 4 5
return process1(help, 1, n);
}
// arr[L...R]上打爆气球,返回最大得分!
// 潜台词 : arr[L-1]这个气球一定没爆arr[R+1]这个气球,一定也没爆!
public static int process1(int[] arr, int L, int R) {
if (L == R) {
return arr[L - 1] * arr[L] * arr[R + 1];
}
// 不止一个气球
// 分析可能性
// 1最后打爆L位置的气球
int p1 = process1(arr, L + 1, R) + arr[L - 1] * arr[L] * arr[R + 1];
// 1最后打爆R位置的气球
int p2 = process1(arr, L, R - 1) + arr[L - 1] * arr[R] * arr[R + 1];
int ans = Math.max(p1, p2);
// 尝试中间每一个气球都最后打爆!
for (int mid = L + 1; mid < R; mid++) {
// arr[mid](最后打爆,一定要最后打爆!)
int cur = process1(arr, L, mid - 1) + process1(arr, mid + 1, R) + arr[L - 1] * arr[mid] * arr[R + 1];
ans = Math.max(ans, cur);
}
return ans;
}
public static int maxScore2(int[] arr) {
int n = arr.length;
int[] help = new int[n + 2];
help[0] = 1;
help[n + 1] = 1;
for (int i = 1; i <= n; i++) {
help[i] = arr[i - 1];
}
int[][] dp = new int[n + 1][n + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
dp[i][j] = -1;
}
}
return process2(help, 1, n, dp);
}
public static int process2(int[] arr, int L, int R, int[][] dp) {
if (dp[L][R] != -1) {
return dp[L][R];
}
int ans = 0;
if (L == R) {
ans = arr[L - 1] * arr[L] * arr[R + 1];
} else {
int p1 = process2(arr, L + 1, R, dp) + arr[L - 1] * arr[L] * arr[R + 1];
int p2 = process2(arr, L, R - 1,dp) + arr[L - 1] * arr[R] * arr[R + 1];
ans = Math.max(p1, p2);
for (int mid = L + 1; mid < R; mid++) {
int cur = process2(arr, L, mid - 1,dp) + process2(arr, mid + 1, R,dp) + arr[L - 1] * arr[mid] * arr[R + 1];
ans = Math.max(ans, cur);
}
}
dp[L][R] = ans;
return ans;
}
public static int maxCoins0(int[] arr) {
// [3,2,1,3]
// [1,3,2,1,3,1]
int N = arr.length;
int[] help = new int[N + 2];
for (int i = 0; i < N; i++) {
help[i + 1] = arr[i];
}
help[0] = 1;
help[N + 1] = 1;
return func(help, 1, N);
}
// L-1位置和R+1位置永远不越界并且[L-1] 和 [R+1] 一定没爆呢!
// 返回arr[L...R]打爆所有气球,最大得分是什么
public static int func(int[] arr, int L, int R) {
if (L == R) {
return arr[L - 1] * arr[L] * arr[R + 1];
}
// 尝试每一种情况,最后打爆的气球,是什么位置
// L...R
// L位置的气球最后打爆
int max = func(arr, L + 1, R) + arr[L - 1] * arr[L] * arr[R + 1];
// R位置的气球最后打爆
max = Math.max(max, func(arr, L, R - 1) + arr[L - 1] * arr[R] * arr[R + 1]);
// 尝试所有L...R中间的位置(L,R)
for (int i = L + 1; i < R; i++) {
// i位置的气球最后打爆
int left = func(arr, L, i - 1);
int right = func(arr, i + 1, R);
int last = arr[L - 1] * arr[i] * arr[R + 1];
int cur = left + right + last;
max = Math.max(max, cur);
}
return max;
}
public static int maxCoins1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return arr[0];
}
int N = arr.length;
int[] help = new int[N + 2];
help[0] = 1;
help[N + 1] = 1;
for (int i = 0; i < N; i++) {
help[i + 1] = arr[i];
}
return process(help, 1, N);
}
// 打爆arr[L..R]范围上的所有气球,返回最大的分数
// 假设arr[L-1]和arr[R+1]一定没有被打爆
public static int process(int[] arr, int L, int R) {
if (L == R) {// 如果arr[L..R]范围上只有一个气球,直接打爆即可
return arr[L - 1] * arr[L] * arr[R + 1];
}
// 最后打爆arr[L]的方案和最后打爆arr[R]的方案,先比较一下
int max = Math.max(arr[L - 1] * arr[L] * arr[R + 1] + process(arr, L + 1, R),
arr[L - 1] * arr[R] * arr[R + 1] + process(arr, L, R - 1));
// 尝试中间位置的气球最后被打爆的每一种方案
for (int i = L + 1; i < R; i++) {
max = Math.max(max, arr[L - 1] * arr[i] * arr[R + 1] + process(arr, L, i - 1) + process(arr, i + 1, R));
}
return max;
}
public static int maxCoins2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
if (arr.length == 1) {
return arr[0];
}
int N = arr.length;
int[] help = new int[N + 2];
help[0] = 1;
help[N + 1] = 1;
for (int i = 0; i < N; i++) {
help[i + 1] = arr[i];
}
int[][] dp = new int[N + 2][N + 2];
for (int i = 1; i <= N; i++) {
dp[i][i] = help[i - 1] * help[i] * help[i + 1];
}
for (int L = N; L >= 1; L--) {
for (int R = L + 1; R <= N; R++) {
int ans = help[L - 1] * help[L] * help[R + 1] + dp[L + 1][R];
ans = Math.max(ans, help[L - 1] * help[R] * help[R + 1] + dp[L][R - 1]);
for (int i = L + 1; i < R; i++) {
ans = Math.max(ans, help[L - 1] * help[i] * help[R + 1] + dp[L][i - 1] + dp[i + 1][R]);
}
dp[L][R] = ans;
}
}
return dp[1][N];
}
}

@ -0,0 +1,67 @@
package 01_mca_08_dp;
// 这个问题leetcode上可以直接测
// 链接https://leetcode.com/problems/longest-common-subsequence/
public class Code07_LongestCommonSubsequence {
public static int longestCommonSubsequence1(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
// 尝试
return process1(str1, str2, str1.length - 1, str2.length - 1);
}
public static int process1(char[] str1, char[] str2, int i, int j) {
if (i == 0 && j == 0) {
return str1[i] == str2[j] ? 1 : 0;
} else if (i == 0) {
if (str1[i] == str2[j]) {
return 1;
} else {
return process1(str1, str2, i, j - 1);
}
} else if (j == 0) {
if (str1[i] == str2[j]) {
return 1;
} else {
return process1(str1, str2, i - 1, j);
}
} else { // i != 0 && j != 0
int p1 = process1(str1, str2, i - 1, j);
int p2 = process1(str1, str2, i, j - 1);
int p3 = str1[i] == str2[j] ? (1 + process1(str1, str2, i - 1, j - 1)) : 0;
return Math.max(p1, Math.max(p2, p3));
}
}
public static int longestCommonSubsequence2(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int N = str1.length;
int M = str2.length;
int[][] dp = new int[N][M];
dp[0][0] = str1[0] == str2[0] ? 1 : 0;
for (int j = 1; j < M; j++) {
dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1];
}
for (int i = 1; i < N; i++) {
dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0];
}
for (int i = 1; i < N; i++) {
for (int j = 1; j < M; j++) {
int p1 = dp[i - 1][j];
int p2 = dp[i][j - 1];
int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0;
dp[i][j] = Math.max(p1, Math.max(p2, p3));
}
}
return dp[N - 1][M - 1];
}
}

@ -0,0 +1,89 @@
package 01_mca_08_dp;
public class Code08_EditCost {
public static int minCost1(String s1, String s2, int ic, int dc, int rc) {
if (s1 == null || s2 == null) {
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int N = str1.length + 1;
int M = str2.length + 1;
int[][] dp = new int[N][M];
// dp[0][0] = 0
for (int i = 1; i < N; i++) {
dp[i][0] = dc * i;
}
for (int j = 1; j < M; j++) {
dp[0][j] = ic * j;
}
for (int i = 1; i < N; i++) {
for (int j = 1; j < M; j++) {
dp[i][j] = dp[i - 1][j - 1] + (str1[i - 1] == str2[j - 1] ? 0 : rc);
dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + ic);
dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + dc);
}
}
return dp[N - 1][M - 1];
}
public static int minCost2(String str1, String str2, int ic, int dc, int rc) {
if (str1 == null || str2 == null) {
return 0;
}
char[] chs1 = str1.toCharArray();
char[] chs2 = str2.toCharArray();
char[] longs = chs1.length >= chs2.length ? chs1 : chs2;
char[] shorts = chs1.length < chs2.length ? chs1 : chs2;
if (chs1.length < chs2.length) {
int tmp = ic;
ic = dc;
dc = tmp;
}
int[] dp = new int[shorts.length + 1];
for (int i = 1; i <= shorts.length; i++) {
dp[i] = ic * i;
}
for (int i = 1; i <= longs.length; i++) {
int pre = dp[0];
dp[0] = dc * i;
for (int j = 1; j <= shorts.length; j++) {
int tmp = dp[j];
if (longs[i - 1] == shorts[j - 1]) {
dp[j] = pre;
} else {
dp[j] = pre + rc;
}
dp[j] = Math.min(dp[j], dp[j - 1] + ic);
dp[j] = Math.min(dp[j], tmp + dc);
pre = tmp;
}
}
return dp[shorts.length];
}
public static void main(String[] args) {
String str1 = "ab12cd3";
String str2 = "abcdf";
System.out.println(minCost1(str1, str2, 5, 3, 2));
System.out.println(minCost2(str1, str2, 5, 3, 2));
str1 = "abcdf";
str2 = "ab12cd3";
System.out.println(minCost1(str1, str2, 3, 2, 4));
System.out.println(minCost2(str1, str2, 3, 2, 4));
str1 = "";
str2 = "ab12cd3";
System.out.println(minCost1(str1, str2, 1, 7, 5));
System.out.println(minCost2(str1, str2, 1, 7, 5));
str1 = "abcdf";
str2 = "";
System.out.println(minCost1(str1, str2, 2, 9, 8));
System.out.println(minCost2(str1, str2, 2, 9, 8));
}
}

@ -0,0 +1,153 @@
package 01_mca_08_dp;
//有m个同样的苹果认为苹果之间无差别
//有n个同样的盘子认为盘子之间也无差别
//还有比如5个苹果如果放进3个盘子
//那么1、3、1和1、1、3和3、1、1的放置方法也认为是一种方法
//如上的设定下,返回有多少种放置方法
//测试链接 : https://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebf
//提交以下的code提交时请把类名改成"Main"
import java.util.Arrays;
import java.util.Scanner;
public class Code09_SplitApples {
public static int test(int apples, int plates) {
int[][] dp = new int[apples + 1][plates + 1];
for (int i = 0; i <= apples; i++) {
for (int j = 0; j <= plates; j++) {
dp[i][j] = -1;
}
}
return f(apples, plates, dp);
}
public static int f(int apples, int plates, int[][] dp) {
if (dp[apples][plates] != -1) {
return dp[apples][plates];
}
int ans = 0;
if (apples == 0) {
ans = 1;
} else if (plates == 0) {
ans = 0;
} else {
if (plates > apples) {
ans = f(apples, apples, dp);
} else { // apples >= plates;
ans = f(apples, plates - 1, dp) + f(apples - plates, plates, dp);
}
}
dp[apples][plates] = ans;
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int m = sc.nextInt();
int n = sc.nextInt();
int ways = ways3(m, n);
System.out.println(ways);
}
sc.close();
}
// 思路来自于分裂数问题
// 体系学习班代码第22节题目3split number问题
public static int ways1(int apples, int plates) {
return process1(1, apples, plates);
}
// pre : 上一个盘子分到的苹果数量当前的盘子分到的数量不能小于pre
// apples : 剩余的苹果数量
// plates : 剩余的盘子数量
// 在盘子够用的情况下,把苹果分完,有几种方法
public static int process1(int pre, int apples, int plates) {
if (apples == 0) {
return 1;
}
// apples != 0
if (plates == 0) {
return 0;
}
// apples != 0 && plates != 0
if (pre > apples) {
return 0;
}
// apples != 0 && plates != 0 && pre <= apples
int way = 0;
// 之前的盘子分了3个苹果现在还剩下8个苹果
// 当前的盘子可以装几个苹果3、4、5、6、7、8
for (int cur = pre; cur <= apples; cur++) {
way += process1(cur, apples - cur, plates - 1);
}
return way;
}
// 新的尝试,最优解
// 苹果有apples个盘子有plates个
// 返回有几种摆法
// 如果苹果数为0有1种摆法什么也不摆
// 如果苹果数不为0但是盘子数为0有0种摆法做不到
// 如果苹果数不为0盘子数也不为0进行如下的情况讨论
// 假设苹果数为apples盘子数为plates
// 可能性 1) apples < plates
// 这种情况下,一定有多余的盘子,这些盘子完全没用,所以砍掉
// 后续是f(apples, apples)
// 可能性 2) apples >= plates
// 在可能性2)下,讨论摆法,有如下两种选择
// 选择a) 不是所有的盘子都使用
// 选择b) 就是所有的盘子都使用
// 对于选择a)既然不是所有盘子都使用那么后续就是f(apples, plates - 1)
// 意思是:既然不是所有盘子都使用,那盘子减少一个,然后继续讨论吧!
// 对于选择b)既然就是所有的盘子都使用那么先把所有盘子都摆上1个苹果。
// 剩余苹果数 = apples - plates
// 然后继续讨论剩下的这些苹果怎么摆进plates个盘子里
// 所以后续是f(apples - plates, plates)
public static int ways2(int apples, int plates) {
if (apples == 0) {
return 1;
}
if (plates == 0) {
return 0;
}
if (plates > apples) {
return ways2(apples, apples);
} else { // apples >= plates;
return ways2(apples, plates - 1) + ways2(apples - plates, plates);
}
}
// 上面最优解尝试的记忆化搜索版本
public static int[][] dp = null;
public static int ways3(int apples, int plates) {
if (dp == null) {
dp = new int[11][11];
for (int i = 0; i <= 10; i++) {
Arrays.fill(dp[i], -1);
}
}
return process3(apples, plates, dp);
}
public static int process3(int apples, int plates, int[][] dp) {
if (dp[apples][plates] != -1) {
return dp[apples][plates];
}
int ans = 0;
if (apples == 0) {
ans = 1;
} else if (plates == 0) {
ans = 0;
} else if (plates > apples) {
ans = process3(apples, apples, dp);
} else {
ans = process3(apples, plates - 1, dp) + process3(apples - plates, plates, dp);
}
dp[apples][plates] = ans;
return ans;
}
}

@ -0,0 +1,109 @@
package 01_mca_08_dp;
public class Code10_HorseJump {
// 当前来到的位置是x,y
// 还剩下rest步需要跳
// 跳完rest步正好跳到ab的方法数是多少
// 10 * 9
public static int jump(int a, int b, int k) {
return process(0, 0, k, a, b);
}
public static int process(int x, int y, int rest, int a, int b) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
if (rest == 0) {
return (x == a && y == b) ? 1 : 0;
}
int ways = process(x + 2, y + 1, rest - 1, a, b);
ways += process(x + 1, y + 2, rest - 1, a, b);
ways += process(x - 1, y + 2, rest - 1, a, b);
ways += process(x - 2, y + 1, rest - 1, a, b);
ways += process(x - 2, y - 1, rest - 1, a, b);
ways += process(x - 1, y - 2, rest - 1, a, b);
ways += process(x + 1, y - 2, rest - 1, a, b);
ways += process(x + 2, y - 1, rest - 1, a, b);
return ways;
}
public static int dp(int a, int b, int k) {
int[][][] dp = new int[10][9][k + 1];
dp[a][b][0] = 1;
for (int rest = 1; rest <= k; rest++) {
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 9; y++) {
int ways = pick(dp, x + 2, y + 1, rest - 1);
ways += pick(dp, x + 1, y + 2, rest - 1);
ways += pick(dp, x - 1, y + 2, rest - 1);
ways += pick(dp, x - 2, y + 1, rest - 1);
ways += pick(dp, x - 2, y - 1, rest - 1);
ways += pick(dp, x - 1, y - 2, rest - 1);
ways += pick(dp, x + 1, y - 2, rest - 1);
ways += pick(dp, x + 2, y - 1, rest - 1);
dp[x][y][rest] = ways;
}
}
}
return dp[0][0][k];
}
public static int pick(int[][][] dp, int x, int y, int rest) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
return dp[x][y][rest];
}
public static int ways(int a, int b, int step) {
return f(0, 0, step, a, b);
}
public static int f(int i, int j, int step, int a, int b) {
if (i < 0 || i > 9 || j < 0 || j > 8) {
return 0;
}
if (step == 0) {
return (i == a && j == b) ? 1 : 0;
}
return f(i - 2, j + 1, step - 1, a, b) + f(i - 1, j + 2, step - 1, a, b) + f(i + 1, j + 2, step - 1, a, b)
+ f(i + 2, j + 1, step - 1, a, b) + f(i + 2, j - 1, step - 1, a, b) + f(i + 1, j - 2, step - 1, a, b)
+ f(i - 1, j - 2, step - 1, a, b) + f(i - 2, j - 1, step - 1, a, b);
}
public static int waysdp(int a, int b, int s) {
int[][][] dp = new int[10][9][s + 1];
dp[a][b][0] = 1;
for (int step = 1; step <= s; step++) { // 按层来
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 9; j++) {
dp[i][j][step] = getValue(dp, i - 2, j + 1, step - 1) + getValue(dp, i - 1, j + 2, step - 1)
+ getValue(dp, i + 1, j + 2, step - 1) + getValue(dp, i + 2, j + 1, step - 1)
+ getValue(dp, i + 2, j - 1, step - 1) + getValue(dp, i + 1, j - 2, step - 1)
+ getValue(dp, i - 1, j - 2, step - 1) + getValue(dp, i - 2, j - 1, step - 1);
}
}
}
return dp[0][0][s];
}
// 在dp表中得到dp[i][j][step]的值,但如果(ij)位置越界的话返回0
public static int getValue(int[][][] dp, int i, int j, int step) {
if (i < 0 || i > 9 || j < 0 || j > 8) {
return 0;
}
return dp[i][j][step];
}
public static void main(String[] args) {
int x = 7;
int y = 7;
int step = 10;
System.out.println(ways(x, y, step));
System.out.println(dp(x, y, step));
System.out.println(jump(x, y, step));
}
}

@ -0,0 +1,21 @@
package 01_test;
// leetcode 121
public class Code01_BestTimeToBuyAndSellStock {
public static int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) {
return 0;
}
// 必须在0时刻卖掉[0] - [0]
int ans = 0;
// arr[0...0]
int min = prices[0];
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
ans = Math.max(ans, prices[i] - min);
}
return ans;
}
}

@ -0,0 +1,121 @@
package 01_test;
// 测试链接https://leetcode.com/problems/longest-palindromic-subsequence/
public class Code01_PalindromeSubsequence {
public static int lpsl1(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
return f(str, 0, str.length - 1);
}
// str[L..R]最长回文子序列长度返回
public static int f(char[] str, int L, int R) {
if (L == R) {
return 1;
}
if (L == R - 1) {
return str[L] == str[R] ? 2 : 1;
}
int p1 = f(str, L + 1, R - 1);
int p2 = f(str, L, R - 1);
int p3 = f(str, L + 1, R);
int p4 = str[L] != str[R] ? 0 : (2 + f(str, L + 1, R - 1));
return Math.max(Math.max(p1, p2), Math.max(p3, p4));
}
public static int lpsl2(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
int[][] dp = new int[N][N];
dp[N - 1][N - 1] = 1;
for (int i = 0; i < N - 1; i++) {
dp[i][i] = 1;
dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
}
for (int L = N - 3; L >= 0; L--) {
for (int R = L + 2; R < N; R++) {
dp[L][R] = Math.max(dp[L][R - 1], dp[L + 1][R]);
if (str[L] == str[R]) {
dp[L][R] = Math.max(dp[L][R], 2 + dp[L + 1][R - 1]);
}
}
}
return dp[0][N - 1];
}
public static int longestPalindromeSubseq1(String s) {
if (s == null || s.length() == 0) {
return 0;
}
if (s.length() == 1) {
return 1;
}
char[] str = s.toCharArray();
char[] reverse = reverse(str);
return longestCommonSubsequence(str, reverse);
}
public static char[] reverse(char[] str) {
int N = str.length;
char[] reverse = new char[str.length];
for (int i = 0; i < str.length; i++) {
reverse[--N] = str[i];
}
return reverse;
}
public static int longestCommonSubsequence(char[] str1, char[] str2) {
int N = str1.length;
int M = str2.length;
int[][] dp = new int[N][M];
dp[0][0] = str1[0] == str2[0] ? 1 : 0;
for (int i = 1; i < N; i++) {
dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0];
}
for (int j = 1; j < M; j++) {
dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1];
}
for (int i = 1; i < N; i++) {
for (int j = 1; j < M; j++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
if (str1[i] == str2[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
return dp[N - 1][M - 1];
}
public static int longestPalindromeSubseq2(String s) {
if (s == null || s.length() == 0) {
return 0;
}
if (s.length() == 1) {
return 1;
}
char[] str = s.toCharArray();
int N = str.length;
int[][] dp = new int[N][N];
dp[N - 1][N - 1] = 1;
for (int i = 0; i < N - 1; i++) {
dp[i][i] = 1;
dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
}
for (int i = N - 3; i >= 0; i--) {
for (int j = i + 2; j < N; j++) {
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
if (str[i] == str[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2);
}
}
}
return dp[0][N - 1];
}
}

@ -0,0 +1,197 @@
package 01_test;
import java.util.Arrays;
// 来自微软面试
// 给定一个正数数组arr长度为n、正数x、正数y
// 你的目标是让arr整体的累加和<=0
// 你可以对数组中的数num执行以下三种操作中的一种且每个数最多能执行一次操作 :
// 1不变
// 2可以选择让num变成0承担x的代价
// 3可以选择让num变成-num承担y的代价
// 返回你达到目标的最小代价
// 数据规模 : 面试时面试官没有说数据规模
public class Code01_SumNoPositiveMinCost {
// 动态规划
public static int minOpStep1(int[] arr, int x, int y) {
int sum = 0;
for (int num : arr) {
sum += num;
}
return process1(arr, x, y, 0, sum);
}
// arr[i...]自由选择,每个位置的数可以执行三种操作中的一种!
// 执行变0的操作x操作代价 -> x
// 执行变相反数的操作y操作代价 -> y
// 还剩下sum这么多累加和需要去搞定
// 返回搞定了sum最低代价是多少
public static int process1(int[] arr, int x, int y, int i, int sum) {
if (sum <= 0) {
return 0;
}
// sum > 0 没搞定
if (i == arr.length) {
return Integer.MAX_VALUE;
}
// 第一选择,什么也不干!
int p1 = process1(arr, x, y, i + 1, sum);
// 第二选择执行x的操作变0 x + 后续
int p2 = Integer.MAX_VALUE;
int next2 = process1(arr, x, y, i + 1, sum - arr[i]);
if (next2 != Integer.MAX_VALUE) {
p2 = x + next2;
}
// 第三选择执行y的操作变相反数 x + 后续 7 -7 -14
int p3 = Integer.MAX_VALUE;
int next3 = process1(arr, x, y, i + 1, sum - (arr[i] << 1));
if (next3 != Integer.MAX_VALUE) {
p3 = y + next3;
}
return Math.min(p1, Math.min(p2, p3));
}
// 贪心(最优解)
public static int minOpStep2(int[] arr, int x, int y) {
Arrays.sort(arr); // 小 -> 大
int n = arr.length;
for (int l = 0, r = n - 1; l <= r; l++, r--) {
int tmp = arr[l];
arr[l] = arr[r];
arr[r] = tmp;
}
// arr 大 -> 小
if (x >= y) { // 没有任何必要执行x操作
int sum = 0;
for (int num : arr) {
sum += num;
}
int cost = 0;
for (int i = 0; i < n && sum > 0; i++) {
sum -= arr[i] << 1;
cost += y;
}
return cost;
} else {
for (int i = n - 2; i >= 0; i--) {
arr[i] += arr[i + 1];
}
int benefit = 0;
// 注意,可以不二分,用不回退的方式!
// 执行Y操作的数有0个的时候
int left = mostLeft(arr, 0, benefit);
int cost = left * x;
for (int i = 0; i < n - 1; i++) {
// 0..i 这些数都执行Y
benefit += arr[i] - arr[i + 1];
left = mostLeft(arr, i + 1, benefit);
cost = Math.min(cost, (i + 1) * y + (left - i - 1) * x);
}
return cost;
}
}
// arr是后缀和数组 arr[l...]中找到值<=v的最左位置
public static int mostLeft(int[] arr, int l, int v) {
int r = arr.length - 1;
int m = 0;
int ans = arr.length;
while (l <= r) {
m = (l + r) / 2;
if (arr[m] <= v) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// 不回退
public static int minOpStep3(int[] arr, int x, int y) {
// 系统排序,小 -> 大
Arrays.sort(arr);
int n = arr.length;
// 如何变成 大 -> 小
for (int l = 0, r = n - 1; l <= r; l++, r--) {
int tmp = arr[l];
arr[l] = arr[r];
arr[r] = tmp;
}
if (x >= y) {
int sum = 0;
for (int num : arr) {
sum += num;
}
int cost = 0;
for (int i = 0; i < n && sum > 0; i++) {
sum -= arr[i] << 1;
cost += y;
}
return cost;
} else {
// 0个数执行Y
int benefit = 0;
// 全部的数都需要执行x才能让累加和<=0
int cost = arr.length * x;
int holdSum = 0;
for (int yRight = 0, holdLeft = n; yRight < holdLeft - 1; yRight++) {
benefit += arr[yRight];
while (holdLeft - 1 > yRight && holdSum + arr[holdLeft - 1] <= benefit) {
holdSum += arr[holdLeft - 1];
holdLeft--;
}
// 0...yRight x holdLeft....
cost = Math.min(cost, (yRight + 1) * y + (holdLeft - yRight - 1) * x);
}
return cost;
}
}
// 为了测试
public static int[] randomArray(int len, int v) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * v) + 1;
}
return arr;
}
// 为了测试
public static int[] copyArray(int[] arr) {
int[] ans = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ans[i] = arr[i];
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int n = 12;
int v = 20;
int c = 10;
int testTime = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * n);
int[] arr = randomArray(len, v);
int[] arr1 = copyArray(arr);
int[] arr2 = copyArray(arr);
int[] arr3 = copyArray(arr);
int x = (int) (Math.random() * c);
int y = (int) (Math.random() * c);
int ans1 = minOpStep1(arr1, x, y);
int ans2 = minOpStep2(arr2, x, y);
int ans3 = minOpStep3(arr3, x, y);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,109 @@
package 01_test;
import java.util.LinkedList;
public class Code02_AllLessNumSubArray {
// 暴力的对数器方法
public static int right(int[] arr, int sum) {
if (arr == null || arr.length == 0 || sum < 0) {
return 0;
}
int N = arr.length;
int count = 0;
for (int L = 0; L < N; L++) {
for (int R = L; R < N; R++) {
int max = arr[L];
int min = arr[L];
for (int i = L + 1; i <= R; i++) {
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
if (max - min <= sum) {
count++;
}
}
}
return count;
}
public static int num(int[] arr, int sum) {
if (arr == null || arr.length == 0 || sum < 0) {
return 0;
}
int N = arr.length;
int count = 0;
LinkedList<Integer> maxWindow = new LinkedList<>();
LinkedList<Integer> minWindow = new LinkedList<>();
int R = 0;
for (int L = 0; L < N; L++) {
while (R < N) {
while (!maxWindow.isEmpty() && arr[maxWindow.peekLast()] <= arr[R]) {
maxWindow.pollLast();
}
maxWindow.addLast(R);
while (!minWindow.isEmpty() && arr[minWindow.peekLast()] >= arr[R]) {
minWindow.pollLast();
}
minWindow.addLast(R);
if (arr[maxWindow.peekFirst()] - arr[minWindow.peekFirst()] > sum) {
break;
} else {
R++;
}
}
count += R - L;
if (maxWindow.peekFirst() == L) {
maxWindow.pollFirst();
}
if (minWindow.peekFirst() == L) {
minWindow.pollFirst();
}
}
return count;
}
// for test
public static int[] generateRandomArray(int maxLen, int maxValue) {
int len = (int) (Math.random() * (maxLen + 1));
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1));
}
return arr;
}
// for test
public static void printArray(int[] arr) {
if (arr != null) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int maxLen = 100;
int maxValue = 200;
int testTime = 100000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxLen, maxValue);
int sum = (int) (Math.random() * (maxValue + 1));
int ans1 = right(arr, sum);
int ans2 = num(arr, sum);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println(sum);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,17 @@
package 01_test;
//leetcode 122
public class Code02_BestTimeToBuyAndSellStockII {
public static int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) {
return 0;
}
int ans = 0;
for (int i = 1; i < prices.length; i++) {
ans += Math.max(prices[i] - prices[i-1], 0);
}
return ans;
}
}

@ -0,0 +1,77 @@
package 01_test;
public class Code02_CoinsWayEveryPaperDifferent {
public static int coinWays(int[] arr, int aim) {
return process(arr, 0, aim);
}
// arr[index....] 组成正好rest这么多的钱有几种方法
public static int process(int[] arr, int index, int rest) {
if (rest < 0) {
return 0;
}
if (index == arr.length) { // 没钱了!
return rest == 0 ? 1 : 0;
} else {
return process(arr, index + 1, rest) + process(arr, index + 1, rest - arr[index]);
}
}
public static int dp(int[] arr, int aim) {
if (aim == 0) {
return 1;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 1;
for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= aim; rest++) {
dp[index][rest] = dp[index + 1][rest] + (rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : 0);
}
}
return dp[0][aim];
}
// 为了测试
public static int[] randomArray(int maxLen, int maxValue) {
int N = (int) (Math.random() * maxLen);
int[] arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = (int) (Math.random() * maxValue) + 1;
}
return arr;
}
// 为了测试
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 为了测试
public static void main(String[] args) {
int maxLen = 20;
int maxValue = 30;
int testTime = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int[] arr = randomArray(maxLen, maxValue);
int aim = (int) (Math.random() * maxValue);
int ans1 = coinWays(arr, aim);
int ans2 = dp(arr, aim);
if (ans1 != ans2) {
System.out.println("Oops!");
printArray(arr);
System.out.println(aim);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,32 @@
package 01_test;
// 本题测试链接 : https://leetcode.com/problems/container-with-most-water/
public class Code02_ContainerWithMostWater {
public static int maxArea1(int[] h) {
int max = 0;
int N = h.length;
for (int i = 0; i < N; i++) { // h[i]
for (int j = i + 1; j < N; j++) { // h[j]
max = Math.max(max, Math.min(h[i], h[j]) * (j - i));
}
}
return max;
}
public static int maxArea2(int[] h) {
int max = 0;
int l = 0;
int r = h.length - 1;
while (l < r) {
max = Math.max(max, Math.min(h[l], h[r]) * (r - l));
if (h[l] > h[r]) {
r--;
} else {
l++;
}
}
return max;
}
}

@ -0,0 +1,123 @@
package 01_test;
public class Code02_ConvertToLetterString {
// str只含有数字字符0~9
// 返回多少种转化方案
public static int number(String str) {
if (str == null || str.length() == 0) {
return 0;
}
return process(str.toCharArray(), 0);
}
// str[0..i-1]转化无需过问
// str[i.....]去转化,返回有多少种转化方法
public static int process(char[] str, int i) {
if (i == str.length) {
return 1;
}
// i没到最后说明有字符
if (str[i] == '0') { // 之前的决定有问题
return 0;
}
// str[i] != '0'
// 可能性一i单转
int ways = process(str, i + 1);
if (i + 1 < str.length && (str[i] - '0') * 10 + str[i + 1] - '0' < 27) {
ways += process(str, i + 2);
}
return ways;
}
// 从右往左的动态规划
// 就是上面方法的动态规划版本
// dp[i]表示str[i...]有多少种转化方式
public static int dp1(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
int[] dp = new int[N + 1];
dp[N] = 1;
for (int i = N - 1; i >= 0; i--) {
if (str[i] != '0') {
int ways = dp[i + 1];
if (i + 1 < str.length && (str[i] - '0') * 10 + str[i + 1] - '0' < 27) {
ways += dp[i + 2];
}
dp[i] = ways;
}
}
return dp[0];
}
// 从左往右的动态规划
// dp[i]表示str[0...i]有多少种转化方式
public static int dp2(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
if (str[0] == '0') {
return 0;
}
int[] dp = new int[N];
dp[0] = 1;
for (int i = 1; i < N; i++) {
if (str[i] == '0') {
// 如果此时str[i]=='0',那么他是一定要拉前一个字符(i-1的字符)一起拼的,
// 那么就要求前一个字符不能也是0否则拼不了。
// 前一个字符不是0就够了嘛不够还得要求拼完了要么是10要么是20如果更大的话拼不了。
// 这就够了嘛还不够你们拼完了还得要求str[0...i-2]真的可以被分解!
// 如果str[0...i-2]都不存在分解方案那i和i-1拼成了也不行因为之前的搞定不了。
if (str[i - 1] == '0' || str[i - 1] > '2' || (i - 2 >= 0 && dp[i - 2] == 0)) {
return 0;
} else {
dp[i] = i - 2 >= 0 ? dp[i - 2] : 1;
}
} else {
dp[i] = dp[i - 1];
if (str[i - 1] != '0' && (str[i - 1] - '0') * 10 + str[i] - '0' <= 26) {
dp[i] += i - 2 >= 0 ? dp[i - 2] : 1;
}
}
}
return dp[N - 1];
}
// 为了测试
public static String randomString(int len) {
char[] str = new char[len];
for (int i = 0; i < len; i++) {
str[i] = (char) ((int) (Math.random() * 10) + '0');
}
return String.valueOf(str);
}
// 为了测试
public static void main(String[] args) {
int N = 30;
int testTime = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * N);
String s = randomString(len);
int ans0 = number(s);
int ans1 = dp1(s);
int ans2 = dp2(s);
if (ans0 != ans1 || ans0 != ans2) {
System.out.println(s);
System.out.println(ans0);
System.out.println(ans1);
System.out.println(ans2);
System.out.println("Oops!");
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,109 @@
package 01_test;
public class Code02_HorseJump {
// 当前来到的位置是x,y
// 还剩下rest步需要跳
// 跳完rest步正好跳到ab的方法数是多少
// 10 * 9
public static int jump(int a, int b, int k) {
return process(0, 0, k, a, b);
}
public static int process(int x, int y, int rest, int a, int b) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
if (rest == 0) {
return (x == a && y == b) ? 1 : 0;
}
int ways = process(x + 2, y + 1, rest - 1, a, b);
ways += process(x + 1, y + 2, rest - 1, a, b);
ways += process(x - 1, y + 2, rest - 1, a, b);
ways += process(x - 2, y + 1, rest - 1, a, b);
ways += process(x - 2, y - 1, rest - 1, a, b);
ways += process(x - 1, y - 2, rest - 1, a, b);
ways += process(x + 1, y - 2, rest - 1, a, b);
ways += process(x + 2, y - 1, rest - 1, a, b);
return ways;
}
public static int dp(int a, int b, int k) {
int[][][] dp = new int[10][9][k + 1];
dp[a][b][0] = 1;
for (int rest = 1; rest <= k; rest++) {
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 9; y++) {
int ways = pick(dp, x + 2, y + 1, rest - 1);
ways += pick(dp, x + 1, y + 2, rest - 1);
ways += pick(dp, x - 1, y + 2, rest - 1);
ways += pick(dp, x - 2, y + 1, rest - 1);
ways += pick(dp, x - 2, y - 1, rest - 1);
ways += pick(dp, x - 1, y - 2, rest - 1);
ways += pick(dp, x + 1, y - 2, rest - 1);
ways += pick(dp, x + 2, y - 1, rest - 1);
dp[x][y][rest] = ways;
}
}
}
return dp[0][0][k];
}
public static int pick(int[][][] dp, int x, int y, int rest) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
return dp[x][y][rest];
}
public static int ways(int a, int b, int step) {
return f(0, 0, step, a, b);
}
public static int f(int i, int j, int step, int a, int b) {
if (i < 0 || i > 9 || j < 0 || j > 8) {
return 0;
}
if (step == 0) {
return (i == a && j == b) ? 1 : 0;
}
return f(i - 2, j + 1, step - 1, a, b) + f(i - 1, j + 2, step - 1, a, b) + f(i + 1, j + 2, step - 1, a, b)
+ f(i + 2, j + 1, step - 1, a, b) + f(i + 2, j - 1, step - 1, a, b) + f(i + 1, j - 2, step - 1, a, b)
+ f(i - 1, j - 2, step - 1, a, b) + f(i - 2, j - 1, step - 1, a, b);
}
public static int waysdp(int a, int b, int s) {
int[][][] dp = new int[10][9][s + 1];
dp[a][b][0] = 1;
for (int step = 1; step <= s; step++) { // 按层来
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 9; j++) {
dp[i][j][step] = getValue(dp, i - 2, j + 1, step - 1) + getValue(dp, i - 1, j + 2, step - 1)
+ getValue(dp, i + 1, j + 2, step - 1) + getValue(dp, i + 2, j + 1, step - 1)
+ getValue(dp, i + 2, j - 1, step - 1) + getValue(dp, i + 1, j - 2, step - 1)
+ getValue(dp, i - 1, j - 2, step - 1) + getValue(dp, i - 2, j - 1, step - 1);
}
}
}
return dp[0][0][s];
}
// 在dp表中得到dp[i][j][step]的值,但如果(ij)位置越界的话返回0
public static int getValue(int[][][] dp, int i, int j, int step) {
if (i < 0 || i > 9 || j < 0 || j > 8) {
return 0;
}
return dp[i][j][step];
}
public static void main(String[] args) {
int x = 7;
int y = 7;
int step = 10;
System.out.println(ways(x, y, step));
System.out.println(dp(x, y, step));
System.out.println(jump(x, y, step));
}
}

@ -0,0 +1,79 @@
package 01_test;
import java.util.PriorityQueue;
public class Code02_LessMoneySplitGold {
// 纯暴力!
public static int lessMoney1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return process(arr, 0);
}
// 等待合并的数都在arr里pre之前的合并行为产生了多少总代价
// arr中只剩一个数字的时候停止合并返回最小的总代价
public static int process(int[] arr, int pre) {
if (arr.length == 1) {
return pre;
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
ans = Math.min(ans, process(copyAndMergeTwo(arr, i, j), pre + arr[i] + arr[j]));
}
}
return ans;
}
public static int[] copyAndMergeTwo(int[] arr, int i, int j) {
int[] ans = new int[arr.length - 1];
int ansi = 0;
for (int arri = 0; arri < arr.length; arri++) {
if (arri != i && arri != j) {
ans[ansi++] = arr[arri];
}
}
ans[ansi] = arr[i] + arr[j];
return ans;
}
public static int lessMoney2(int[] arr) {
PriorityQueue<Integer> pQ = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
pQ.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (pQ.size() > 1) {
cur = pQ.poll() + pQ.poll();
sum += cur;
pQ.add(cur);
}
return sum;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * (maxValue + 1));
}
return arr;
}
public static void main(String[] args) {
int testTime = 100000;
int maxSize = 6;
int maxValue = 1000;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
if (lessMoney1(arr) != lessMoney2(arr)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}

@ -0,0 +1,121 @@
package 01_test;
public class Code02_MinCoinsNoLimit {
public static int minCoins(int[] arr, int aim) {
return process(arr, 0, aim);
}
// arr[index...]面值,每种面值张数自由选择,
// 搞出rest正好这么多钱返回最小张数
// 拿Integer.MAX_VALUE标记怎么都搞定不了
public static int process(int[] arr, int index, int rest) {
if (index == arr.length) {
return rest == 0 ? 0 : Integer.MAX_VALUE;
} else {
int ans = Integer.MAX_VALUE;
for (int zhang = 0; zhang * arr[index] <= rest; zhang++) {
int next = process(arr, index + 1, rest - zhang * arr[index]);
if (next != Integer.MAX_VALUE) {
ans = Math.min(ans, zhang + next);
}
}
return ans;
}
}
public static int dp1(int[] arr, int aim) {
if (aim == 0) {
return 0;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 0;
for (int j = 1; j <= aim; j++) {
dp[N][j] = Integer.MAX_VALUE;
}
for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= aim; rest++) {
int ans = Integer.MAX_VALUE;
for (int zhang = 0; zhang * arr[index] <= rest; zhang++) {
int next = dp[index + 1][rest - zhang * arr[index]];
if (next != Integer.MAX_VALUE) {
ans = Math.min(ans, zhang + next);
}
}
dp[index][rest] = ans;
}
}
return dp[0][aim];
}
public static int dp2(int[] arr, int aim) {
if (aim == 0) {
return 0;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 0;
for (int j = 1; j <= aim; j++) {
dp[N][j] = Integer.MAX_VALUE;
}
for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= aim; rest++) {
dp[index][rest] = dp[index + 1][rest];
if (rest - arr[index] >= 0
&& dp[index][rest - arr[index]] != Integer.MAX_VALUE) {
dp[index][rest] = Math.min(dp[index][rest], dp[index][rest - arr[index]] + 1);
}
}
}
return dp[0][aim];
}
// 为了测试
public static int[] randomArray(int maxLen, int maxValue) {
int N = (int) (Math.random() * maxLen);
int[] arr = new int[N];
boolean[] has = new boolean[maxValue + 1];
for (int i = 0; i < N; i++) {
do {
arr[i] = (int) (Math.random() * maxValue) + 1;
} while (has[arr[i]]);
has[arr[i]] = true;
}
return arr;
}
// 为了测试
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 为了测试
public static void main(String[] args) {
int maxLen = 20;
int maxValue = 30;
int testTime = 300000;
System.out.println("功能测试开始");
for (int i = 0; i < testTime; i++) {
int N = (int) (Math.random() * maxLen);
int[] arr = randomArray(N, maxValue);
int aim = (int) (Math.random() * maxValue);
int ans1 = minCoins(arr, aim);
int ans2 = dp1(arr, aim);
int ans3 = dp2(arr, aim);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("Oops!");
printArray(arr);
System.out.println(aim);
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("功能测试结束");
}
}

@ -0,0 +1,35 @@
package 01_test;
// 本题测试链接 : https://leetcode.com/problems/maximum-subarray/
public class Code02_SubArrayMaxSum {
public static int maxSubArray(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int max = Integer.MIN_VALUE;
int cur = 0;
for (int i = 0; i < arr.length; i++) {
cur += arr[i];
max = Math.max(max, cur);
cur = cur < 0 ? 0 : cur;
}
return max;
}
public static int maxSubArray2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
// 上一步dp的值
// dp[0]
int pre = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
pre = Math.max(arr[i], arr[i] + pre);
max = Math.max(max, pre);
}
return max;
}
}

@ -0,0 +1,23 @@
package 01_test;
//leetcode 123
public class Code03_BestTimeToBuyAndSellStockIII {
public static int maxProfit(int[] prices) {
if (prices == null || prices.length < 2) {
return 0;
}
int ans = 0;
int doneOnceMinusBuyMax = -prices[0];
int doneOnceMax = 0;
int min = prices[0];
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
ans = Math.max(ans, doneOnceMinusBuyMax + prices[i]);
doneOnceMax = Math.max(doneOnceMax, prices[i] - min);
doneOnceMinusBuyMax = Math.max(doneOnceMinusBuyMax, doneOnceMax - prices[i]);
}
return ans;
}
}

@ -0,0 +1,108 @@
package 01_test;
public class Code03_CoinsWayNoLimit {
public static int coinsWay(int[] arr, int aim) {
if (arr == null || arr.length == 0 || aim < 0) {
return 0;
}
return process(arr, 0, aim);
}
// arr[index....] 所有的面值每一个面值都可以任意选择张数组成正好rest这么多钱方法数多少
public static int process(int[] arr, int index, int rest) {
if (index == arr.length) { // 没钱了
return rest == 0 ? 1 : 0;
}
int ways = 0;
for (int zhang = 0; zhang * arr[index] <= rest; zhang++) {
ways += process(arr, index + 1, rest - (zhang * arr[index]));
}
return ways;
}
public static int dp1(int[] arr, int aim) {
if (arr == null || arr.length == 0 || aim < 0) {
return 0;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 1;
for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= aim; rest++) {
int ways = 0;
for (int zhang = 0; zhang * arr[index] <= rest; zhang++) {
ways += dp[index + 1][rest - (zhang * arr[index])];
}
dp[index][rest] = ways;
}
}
return dp[0][aim];
}
public static int dp2(int[] arr, int aim) {
if (arr == null || arr.length == 0 || aim < 0) {
return 0;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 1;
for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= aim; rest++) {
dp[index][rest] = dp[index + 1][rest];
if (rest - arr[index] >= 0) {
dp[index][rest] += dp[index][rest - arr[index]];
}
}
}
return dp[0][aim];
}
// 为了测试
public static int[] randomArray(int maxLen, int maxValue) {
int N = (int) (Math.random() * maxLen);
int[] arr = new int[N];
boolean[] has = new boolean[maxValue + 1];
for (int i = 0; i < N; i++) {
do {
arr[i] = (int) (Math.random() * maxValue) + 1;
} while (has[arr[i]]);
has[arr[i]] = true;
}
return arr;
}
// 为了测试
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 为了测试
public static void main(String[] args) {
int maxLen = 10;
int maxValue = 30;
int testTime = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int[] arr = randomArray(maxLen, maxValue);
int aim = (int) (Math.random() * maxValue);
int ans1 = coinsWay(arr, aim);
int ans2 = dp1(arr, aim);
int ans3 = dp2(arr, aim);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("Oops!");
printArray(arr);
System.out.println(aim);
System.out.println(ans1);
System.out.println(ans2);
System.out.println(ans3);
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,85 @@
package 01_test;
public class Code03_SplitNumber {
// n为正数
public static int ways(int n) {
if (n < 0) {
return 0;
}
if (n == 1) {
return 1;
}
return process(1, n);
}
// 上一个拆出来的数是pre
// 还剩rest需要去拆
// 返回拆解的方法数
public static int process(int pre, int rest) {
if (rest == 0) {
return 1;
}
if (pre > rest) {
return 0;
}
int ways = 0;
for (int first = pre; first <= rest; first++) {
ways += process(first, rest - first);
}
return ways;
}
public static int dp1(int n) {
if (n < 0) {
return 0;
}
if (n == 1) {
return 1;
}
int[][] dp = new int[n + 1][n + 1];
for (int pre = 1; pre <= n; pre++) {
dp[pre][0] = 1;
dp[pre][pre] = 1;
}
for (int pre = n - 1; pre >= 1; pre--) {
for (int rest = pre + 1; rest <= n; rest++) {
int ways = 0;
for (int first = pre; first <= rest; first++) {
ways += dp[first][rest - first];
}
dp[pre][rest] = ways;
}
}
return dp[1][n];
}
public static int dp2(int n) {
if (n < 0) {
return 0;
}
if (n == 1) {
return 1;
}
int[][] dp = new int[n + 1][n + 1];
for (int pre = 1; pre <= n; pre++) {
dp[pre][0] = 1;
dp[pre][pre] = 1;
}
for (int pre = n - 1; pre >= 1; pre--) {
for (int rest = pre + 1; rest <= n; rest++) {
dp[pre][rest] = dp[pre + 1][rest];
dp[pre][rest] += dp[pre][rest - pre];
}
}
return dp[1][n];
}
public static void main(String[] args) {
int test = 39;
System.out.println(ways(test));
System.out.println(dp1(test));
System.out.println(dp2(test));
}
}

@ -0,0 +1,75 @@
package 01_test;
public class Code03_SubMatrixMaxSum {
public static int maxSum(int[][] m) {
if (m == null || m.length == 0 || m[0].length == 0) {
return 0;
}
// O(N^2 * M)
int N = m.length;
int M = m[0].length;
int max = Integer.MIN_VALUE;
for (int i = 0; i < N; i++) {
// i~j
int[] s = new int[M];
for (int j = i; j < N; j++) {
for (int k = 0; k < M; k++) {
s[k] += m[j][k];
}
max = Math.max(max, maxSubArray(s));
}
}
return max;
}
public static int maxSubArray(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int max = Integer.MIN_VALUE;
int cur = 0;
for (int i = 0; i < arr.length; i++) {
cur += arr[i];
max = Math.max(max, cur);
cur = cur < 0 ? 0 : cur;
}
return max;
}
// 本题测试链接 : https://leetcode-cn.com/problems/max-submatrix-lcci/
public static int[] getMaxMatrix(int[][] m) {
int N = m.length;
int M = m[0].length;
int max = Integer.MIN_VALUE;
int cur = 0;
int a = 0;
int b = 0;
int c = 0;
int d = 0;
for (int i = 0; i < N; i++) {
int[] s = new int[M];
for (int j = i; j < N; j++) {
cur = 0;
int begin = 0;
for (int k = 0; k < M; k++) {
s[k] += m[j][k];
cur += s[k];
if (max < cur) {
max = cur;
a = i;
b = begin;
c = j;
d = k;
}
if (cur < 0) {
cur = 0;
begin = k + 1;
}
}
}
}
return new int[] { a, b, c, d };
}
}

@ -0,0 +1,76 @@
package 01_test;
import java.util.PriorityQueue;
// 本题测试链接 : https://leetcode.com/problems/trapping-rain-water-ii/
public class Code03_TrappingRainWaterII {
public static class Node {
public int value;
public int row;
public int col;
public Node(int v, int r, int c) {
value = v;
row = r;
col = c;
}
}
public static int trapRainWater(int[][] heightMap) {
if (heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0) {
return 0;
}
int N = heightMap.length;
int M = heightMap[0].length;
boolean[][] isEnter = new boolean[N][M];
PriorityQueue<Node> heap = new PriorityQueue<>((a, b) -> a.value - b.value);
for (int col = 0; col < M - 1; col++) {
isEnter[0][col] = true;
heap.add(new Node(heightMap[0][col], 0, col));
}
for (int row = 0; row < N - 1; row++) {
isEnter[row][M - 1] = true;
heap.add(new Node(heightMap[row][M - 1], row, M - 1));
}
for (int col = M - 1; col > 0; col--) {
isEnter[N - 1][col] = true;
heap.add(new Node(heightMap[N - 1][col], N - 1, col));
}
for (int row = N - 1; row > 0; row--) {
isEnter[row][0] = true;
heap.add(new Node(heightMap[row][0], row, 0));
}
int water = 0;
int max = 0;
while (!heap.isEmpty()) {
Node cur = heap.poll();
max = Math.max(max, cur.value);
int r = cur.row;
int c = cur.col;
if (r > 0 && !isEnter[r - 1][c]) {
water += Math.max(0, max - heightMap[r - 1][c]);
isEnter[r - 1][c] = true;
heap.add(new Node(heightMap[r - 1][c], r - 1, c));
}
if (r < N - 1 && !isEnter[r + 1][c]) {
water += Math.max(0, max - heightMap[r + 1][c]);
isEnter[r + 1][c] = true;
heap.add(new Node(heightMap[r + 1][c], r + 1, c));
}
if (c > 0 && !isEnter[r][c - 1]) {
water += Math.max(0, max - heightMap[r][c - 1]);
isEnter[r][c - 1] = true;
heap.add(new Node(heightMap[r][c - 1], r, c - 1));
}
if (c < M - 1 && !isEnter[r][c + 1]) {
water += Math.max(0, max - heightMap[r][c + 1]);
isEnter[r][c + 1] = true;
heap.add(new Node(heightMap[r][c + 1], r, c + 1));
}
}
return water;
}
}

@ -0,0 +1,65 @@
package 01_test;
//leetcode 188
public class Code04_BestTimeToBuyAndSellStockIV {
public static int maxProfit(int K, int[] prices) {
if (prices == null || prices.length == 0) {
return 0;
}
int N = prices.length;
if (K >= N / 2) {
return allTrans(prices);
}
int[][] dp = new int[K + 1][N];
int ans = 0;
for (int tran = 1; tran <= K; tran++) {
int pre = dp[tran][0];
int best = pre - prices[0];
for (int index = 1; index < N; index++) {
pre = dp[tran - 1][index];
dp[tran][index] = Math.max(dp[tran][index - 1], prices[index] + best);
best = Math.max(best, pre - prices[index]);
ans = Math.max(dp[tran][index], ans);
}
}
return ans;
}
public static int allTrans(int[] prices) {
int ans = 0;
for (int i = 1; i < prices.length; i++) {
ans += Math.max(prices[i] - prices[i - 1], 0);
}
return ans;
}
// 课上写的版本,对了
public static int maxProfit2(int K, int[] arr) {
if (arr == null || arr.length == 0 || K < 1) {
return 0;
}
int N = arr.length;
if (K >= N / 2) {
return allTrans(arr);
}
int[][] dp = new int[N][K + 1];
// dp[...][0] = 0
// dp[0][...] = arr[0.0] 0
for (int j = 1; j <= K; j++) {
// dp[1][j]
int p1 = dp[0][j];
int best = Math.max(dp[1][j - 1] - arr[1], dp[0][j - 1] - arr[0]);
dp[1][j] = Math.max(p1, best + arr[1]);
// dp[1][j] 准备好一些枚举,接下来准备好的枚举
for (int i = 2; i < N; i++) {
p1 = dp[i - 1][j];
int newP = dp[i][j - 1] - arr[i];
best = Math.max(newP, best);
dp[i][j] = Math.max(p1, best + arr[i]);
}
}
return dp[N - 1][K];
}
}

@ -0,0 +1,100 @@
package 01_test;
public class Code04_DistinctSubseq {
public static int numDistinct1(String S, String T) {
char[] s = S.toCharArray();
char[] t = T.toCharArray();
return process(s, t, s.length, t.length);
}
public static int process(char[] s, char[] t, int i, int j) {
if (j == 0) {
return 1;
}
if (i == 0) {
return 0;
}
int res = process(s, t, i - 1, j);
if (s[i - 1] == t[j - 1]) {
res += process(s, t, i - 1, j - 1);
}
return res;
}
// S[...i]的所有子序列中包含多少个字面值等于T[...j]这个字符串的子序列
// 记为dp[i][j]
// 可能性1S[...i]的所有子序列中都不以s[i]结尾则dp[i][j]肯定包含dp[i-1][j]
// 可能性2S[...i]的所有子序列中都必须以s[i]结尾,
// 这要求S[i] == T[j]则dp[i][j]包含dp[i-1][j-1]
public static int numDistinct2(String S, String T) {
char[] s = S.toCharArray();
char[] t = T.toCharArray();
// dp[i][j] : s[0..i] T[0...j]
// dp[i][j] : s只拿前i个字符做子序列有多少个子序列字面值等于T的前j个字符的前缀串
int[][] dp = new int[s.length + 1][t.length + 1];
// dp[0][0]
// dp[0][j] = s只拿前0个字符做子序列, T前j个字符
for (int j = 0; j <= t.length; j++) {
dp[0][j] = 0;
}
for (int i = 0; i <= s.length; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= s.length; i++) {
for (int j = 1; j <= t.length; j++) {
dp[i][j] = dp[i - 1][j] + (s[i - 1] == t[j - 1] ? dp[i - 1][j - 1] : 0);
}
}
return dp[s.length][t.length];
}
public static int numDistinct3(String S, String T) {
char[] s = S.toCharArray();
char[] t = T.toCharArray();
int[] dp = new int[t.length + 1];
dp[0] = 1;
for (int j = 1; j <= t.length; j++) {
dp[j] = 0;
}
for (int i = 1; i <= s.length; i++) {
for (int j = t.length; j >= 1; j--) {
dp[j] += s[i - 1] == t[j - 1] ? dp[j - 1] : 0;
}
}
return dp[t.length];
}
public static int dp(String S, String T) {
char[] s = S.toCharArray();
char[] t = T.toCharArray();
int N = s.length;
int M = t.length;
int[][] dp = new int[N][M];
// s[0..0] T[0..0] dp[0][0]
dp[0][0] = s[0] == t[0] ? 1 : 0;
for (int i = 1; i < N; i++) {
dp[i][0] = s[i] == t[0] ? (dp[i - 1][0] + 1) : dp[i - 1][0];
}
for (int i = 1; i < N; i++) {
for (int j = 1; j <= Math.min(i, M - 1); j++) {
dp[i][j] = dp[i - 1][j];
if (s[i] == t[j]) {
dp[i][j] += dp[i - 1][j - 1];
}
}
}
return dp[N - 1][M - 1];
}
public static void main(String[] args) {
String S = "1212311112121132";
String T = "13";
System.out.println(numDistinct3(S, T));
System.out.println(dp(S, T));
}
}

@ -0,0 +1,67 @@
package 01_test;
// 这个问题leetcode上可以直接测
// 链接https://leetcode.com/problems/longest-common-subsequence/
public class Code04_LongestCommonSubsequence {
public static int longestCommonSubsequence1(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
// 尝试
return process1(str1, str2, str1.length - 1, str2.length - 1);
}
public static int process1(char[] str1, char[] str2, int i, int j) {
if (i == 0 && j == 0) {
return str1[i] == str2[j] ? 1 : 0;
} else if (i == 0) {
if (str1[i] == str2[j]) {
return 1;
} else {
return process1(str1, str2, i, j - 1);
}
} else if (j == 0) {
if (str1[i] == str2[j]) {
return 1;
} else {
return process1(str1, str2, i - 1, j);
}
} else { // i != 0 && j != 0
int p1 = process1(str1, str2, i - 1, j);
int p2 = process1(str1, str2, i, j - 1);
int p3 = str1[i] == str2[j] ? (1 + process1(str1, str2, i - 1, j - 1)) : 0;
return Math.max(p1, Math.max(p2, p3));
}
}
public static int longestCommonSubsequence2(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int N = str1.length;
int M = str2.length;
int[][] dp = new int[N][M];
dp[0][0] = str1[0] == str2[0] ? 1 : 0;
for (int j = 1; j < M; j++) {
dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1];
}
for (int i = 1; i < N; i++) {
dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0];
}
for (int i = 1; i < N; i++) {
for (int j = 1; j < M; j++) {
int p1 = dp[i - 1][j];
int p2 = dp[i][j - 1];
int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0;
dp[i][j] = Math.max(p1, Math.max(p2, p3));
}
}
return dp[N - 1][M - 1];
}
}

@ -0,0 +1,135 @@
package 01_test;
// 测试链接 : https://leetcode.com/problems/regular-expression-matching/
public class Code04_RegularExpressionMatch {
public static boolean isValid(char[] s, char[] e) {
// s中不能有'.' or '*'
for (int i = 0; i < s.length; i++) {
if (s[i] == '*' || s[i] == '.') {
return false;
}
}
// 开头的e[0]不能是'*',没有相邻的'*'
for (int i = 0; i < e.length; i++) {
if (e[i] == '*' && (i == 0 || e[i - 1] == '*')) {
return false;
}
}
return true;
}
// 初始尝试版本,不包含斜率优化
public static boolean isMatch1(String str, String exp) {
if (str == null || exp == null) {
return false;
}
char[] s = str.toCharArray();
char[] e = exp.toCharArray();
return isValid(s, e) && process(s, e, 0, 0);
}
// str[si.....] 能不能被 exp[ei.....]配出来! true false
public static boolean process(char[] s, char[] e, int si, int ei) {
if (ei == e.length) { // exp 没了 str
return si == s.length;
}
// exp[ei]还有字符
// ei + 1位置的字符不是*
if (ei + 1 == e.length || e[ei + 1] != '*') {
// ei + 1 不是*
// str[si] 必须和 exp[ei] 能配上!
return si != s.length && (e[ei] == s[si] || e[ei] == '.') && process(s, e, si + 1, ei + 1);
}
// exp[ei]还有字符
// ei + 1位置的字符是*!
while (si != s.length && (e[ei] == s[si] || e[ei] == '.')) {
if (process(s, e, si, ei + 2)) {
return true;
}
si++;
}
return process(s, e, si, ei + 2);
}
// 改记忆化搜索+斜率优化
public static boolean isMatch2(String str, String exp) {
if (str == null || exp == null) {
return false;
}
char[] s = str.toCharArray();
char[] e = exp.toCharArray();
if (!isValid(s, e)) {
return false;
}
int[][] dp = new int[s.length + 1][e.length + 1];
// dp[i][j] = 0, 没算过!
// dp[i][j] = -1 算过返回值是false
// dp[i][j] = 1 算过返回值是true
return isValid(s, e) && process2(s, e, 0, 0, dp);
}
public static boolean process2(char[] s, char[] e, int si, int ei, int[][] dp) {
if (dp[si][ei] != 0) {
return dp[si][ei] == 1;
}
boolean ans = false;
if (ei == e.length) {
ans = si == s.length;
} else {
if (ei + 1 == e.length || e[ei + 1] != '*') {
ans = si != s.length && (e[ei] == s[si] || e[ei] == '.') && process2(s, e, si + 1, ei + 1, dp);
} else {
if (si == s.length) { // ei ei+1 *
ans = process2(s, e, si, ei + 2, dp);
} else { // si没结束
if (s[si] != e[ei] && e[ei] != '.') {
ans = process2(s, e, si, ei + 2, dp);
} else { // s[si] 可以和 e[ei]配上
ans = process2(s, e, si, ei + 2, dp) || process2(s, e, si + 1, ei, dp);
}
}
}
}
dp[si][ei] = ans ? 1 : -1;
return ans;
}
// 动态规划版本 + 斜率优化
public static boolean isMatch3(String str, String pattern) {
if (str == null || pattern == null) {
return false;
}
char[] s = str.toCharArray();
char[] p = pattern.toCharArray();
if (!isValid(s, p)) {
return false;
}
int N = s.length;
int M = p.length;
boolean[][] dp = new boolean[N + 1][M + 1];
dp[N][M] = true;
for (int j = M - 1; j >= 0; j--) {
dp[N][j] = (j + 1 < M && p[j + 1] == '*') && dp[N][j + 2];
}
// dp[0..N-2][M-1]都等于false只有dp[N-1][M-1]需要讨论
if (N > 0 && M > 0) {
dp[N - 1][M - 1] = (s[N - 1] == p[M - 1] || p[M - 1] == '.');
}
for (int i = N - 1; i >= 0; i--) {
for (int j = M - 2; j >= 0; j--) {
if (p[j + 1] != '*') {
dp[i][j] = ((s[i] == p[j]) || (p[j] == '.')) && dp[i + 1][j + 1];
} else {
if ((s[i] == p[j] || p[j] == '.') && dp[i + 1][j]) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i][j + 2];
}
}
}
}
return dp[0][0];
}
}

@ -0,0 +1,193 @@
package 01_test;
// leetcode原题
// 测试链接https://leetcode.com/problems/split-array-largest-sum/
public class Code04_SplitArrayLargestSum {
// 求原数组arr[L...R]的累加和
public static int sum(int[] sum, int L, int R) {
return sum[R + 1] - sum[L];
}
// 不优化枚举的动态规划方法O(N^2 * K)
public static int splitArray1(int[] nums, int K) {
int N = nums.length;
int[] sum = new int[N + 1];
for (int i = 0; i < N; i++) {
sum[i + 1] = sum[i] + nums[i];
}
int[][] dp = new int[N][K + 1];
for (int j = 1; j <= K; j++) {
dp[0][j] = nums[0];
}
for (int i = 1; i < N; i++) {
dp[i][1] = sum(sum, 0, i);
}
// 每一行从上往下
// 每一列从左往右
// 根本不去凑优化位置对儿!
for (int i = 1; i < N; i++) {
for (int j = 2; j <= K; j++) {
int ans = Integer.MAX_VALUE;
// 枚举是完全不优化的!
for (int leftEnd = 0; leftEnd <= i; leftEnd++) {
int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1];
int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i);
int cur = Math.max(leftCost, rightCost);
if (cur < ans) {
ans = cur;
}
}
dp[i][j] = ans;
}
}
return dp[N - 1][K];
}
// 课上现场写的方法用了枚举优化O(N * K)
public static int splitArray2(int[] nums, int K) {
int N = nums.length;
int[] sum = new int[N + 1];
for (int i = 0; i < N; i++) {
sum[i + 1] = sum[i] + nums[i];
}
int[][] dp = new int[N][K + 1];
int[][] best = new int[N][K + 1];
for (int j = 1; j <= K; j++) {
dp[0][j] = nums[0];
best[0][j] = -1;
}
for (int i = 1; i < N; i++) {
dp[i][1] = sum(sum, 0, i);
best[i][1] = -1;
}
// 从第2列开始从左往右
// 每一列,从下往上
// 为什么这样的顺序?因为要去凑(左,下)优化位置对儿!
for (int j = 2; j <= K; j++) {
for (int i = N - 1; i >= 1; i--) {
int down = best[i][j - 1];
// 如果i==N-1则不优化上限
int up = i == N - 1 ? N - 1 : best[i + 1][j];
int ans = Integer.MAX_VALUE;
int bestChoose = -1;
for (int leftEnd = down; leftEnd <= up; leftEnd++) {
int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1];
int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i);
int cur = Math.max(leftCost, rightCost);
// 注意下面的if一定是 < 课上的错误就是此处!当时写的 <=
// 也就是说,只有取得明显的好处才移动!
// 举个例子来说明,比如[2,6,4,4]3个画匠时候如下两种方案都是最优:
// (2,6) (4) 两个画匠负责 | (4) 最后一个画匠负责
// (2,6) (4,4)两个画匠负责 | 最后一个画匠什么也不负责
// 第一种方案划分为,[0~2] [3~3]
// 第二种方案划分为,[0~3] [无]
// 两种方案的答案都是8但是划分点位置一定不要移动!
// 只有明显取得好处时(<),划分点位置才移动!
// 也就是说后面的方案如果==前面的最优,不要移动!只有优于前面的最优,才移动
// 比如上面的两个方案,如果你移动到了方案二,你会得到:
// [2,6,4,4] 三个画匠时,最优为[0~3](前两个画家) [无](最后一个画家)
// 最优划分点为3位置(best[3][3])
// 那么当4个画匠时也就是求解dp[3][4]时
// 因为best[3][3] = 3这个值提供了dp[3][4]的下限
// 而事实上dp[3][4]的最优划分为:
// [0~2](三个画家处理) [3~3] (一个画家处理)此时最优解为6
// 所以你就得不到dp[3][4]的最优解了因为划分点已经越过2了
// 提供了对数器验证,你可以改成<=对数器和leetcode都过不了
// 这里是<对数器和leetcode都能通过
// 这里面会让同学们感到困惑的点:
// 为啥==的时候,不移动,只有<的时候,才移动呢?例子懂了,但是道理何在?
// 哈哈哈哈哈看了邮局选址问题你更懵请看42节
if (cur < ans) {
ans = cur;
bestChoose = leftEnd;
}
}
dp[i][j] = ans;
best[i][j] = bestChoose;
}
}
return dp[N - 1][K];
}
public static int splitArray3(int[] nums, int M) {
long sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
long l = 0;
long r = sum;
long ans = 0;
while (l <= r) {
long mid = (l + r) / 2;
long cur = getNeedParts(nums, mid);
if (cur <= M) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return (int) ans;
}
public static int getNeedParts(int[] arr, long aim) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] > aim) {
return Integer.MAX_VALUE;
}
}
int parts = 1;
int all = arr[0];
for (int i = 1; i < arr.length; i++) {
if (all + arr[i] > aim) {
parts++;
all = arr[i];
} else {
all += arr[i];
}
}
return parts;
}
public static int[] randomArray(int len, int maxValue) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * maxValue);
}
return arr;
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int N = 100;
int maxValue = 100;
int testTime = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * N) + 1;
int M = (int) (Math.random() * N) + 1;
int[] arr = randomArray(len, maxValue);
int ans1 = splitArray1(arr, M);
int ans2 = splitArray2(arr, M);
int ans3 = splitArray3(arr, M);
if (ans1 != ans2 || ans1 != ans3) {
System.out.print("arr : ");
printArray(arr);
System.out.println("M : " + M);
System.out.println("ans1 : " + ans1);
System.out.println("ans2 : " + ans2);
System.out.println("ans3 : " + ans3);
System.out.println("Oops!");
break;
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,101 @@
package 01_test;
//leetcode 309
public class Code05_BestTimeToBuyAndSellStockWithCooldown {
// 该方法是对的,提交之后大量的测试用例通过,最后几个会超时
// 如果把这个方法改成动态规划,是可以通过的,但这个尝试不是最优解
public static int maxProfit1(int[] prices) {
return process1(prices, false, 0, 0);
}
// buy == false 目前可以交易,而且当前没有购买行为
// buy == true 已经买了买入价buyPrices待卖出
public static int process1(int[] prices, boolean buy, int index, int buyPrices) {
if (index >= prices.length) {
return 0;
}
if (buy) {
int noSell = process1(prices, true, index + 1, buyPrices);
int yesSell = prices[index] - buyPrices + process1(prices, false, index + 2, 0);
return Math.max(noSell, yesSell);
} else {
int noBuy = process1(prices, false, index + 1, 0);
int yesBuy = process1(prices, true, index + 1, prices[index]);
return Math.max(noBuy, yesBuy);
}
}
// 最优尝试如下:
// buy[i] : 在0...i范围上最后一次操作是buy动作
// 这最后一次操作有可能发生在i位置也可能发生在i之前
// buy[i]值的含义是max{ 所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格] }
// 比如arr[0...i]假设为[1,3,4,6,2,7,1...i之后的数字不用管]
// 什么叫,所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格]
// 比如其中一种可能性:
// 假设最后一次buy动作发生在2这个数的时候那么之前的交易只能在[1,3,4]上结束因为6要cooldown的
// 此时最大收益是多少呢是4-1==3。那么之前交易获得的最大收益 - 最后buy动作的收购价格 = 3 - 2 = 1
// 另一种可能性:
// 再比如最后一次buy动作发生在最后的1这个数的时候那么之前的交易只能在[1,3,4,6,2]上发生因为7要cooldown的
// 此时最大收益是多少呢是6-1==5。那么之前交易获得的最大收益 - 最后buy动作的收购价格 = 5 - 1 = 4
// 除了上面两种可能性之外还有很多可能性你可以假设每个数字都是最后buy动作的时候
// 所有可能性中,(之前交易获得的最大收益 - 最后buy动作的收购价格)的最大值就是buy[i]的含义
// 为啥buy[i]要算之前的最大收益 - 最后一次收购价格?尤其是最后为什么要减这么一下?
// 因为这样一来当你之后以X价格做成一笔交易的时候当前最好的总收益直接就是 X + buy[i]了
//
// sell[i] :0...i范围上最后一次操作是sell动作这最后一次操作有可能发生在i位置也可能发生在之前
// sell[i]值的含义0...i范围上最后一次动作是sell的情况下最好的收益
//
// 于是通过分析,能得到以下的转移方程:
// buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i])
// 如果i位置没有发生buy行为说明有没有i位置都一样那么buy[i] = buy[i-1],这显而易见
// 如果i位置发生了buy行为, 那么buy[i] = sell[i - 2] - prices[i]
// 因为你想在i位置买的话你必须保证之前交易行为发生在0...i-2上
// 因为如果i-1位置有可能参与交易的话i位置就要cooldown了
// 而且之前交易行为必须以sell结束你才能buy而且要保证之前交易尽可能得到最好的利润
// 这正好是sell[i - 2]所代表的含义并且根据buy[i]的定义,最后一定要 - prices[i]
//
// sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i])
// 如果i位置没有发生sell行为那么sell[i] = sell[i-1],这显而易见
// 如果i位置发生了sell行为那么我们一定要找到 {之前获得尽可能好的收益 - 最后一次的收购价格尽可能低}
// 而这正好是buy[i - 1]的含义!之前所有的"尽可能"中,最好的一个!
public static int maxProfit2(int[] prices) {
if (prices.length < 2) {
return 0;
}
int N = prices.length;
int[] buy = new int[N];
int[] sell = new int[N];
// buy[0] 不需要设置 buy[0] = -arr[0]
// sell[0] = 0
buy[1] = Math.max(-prices[0], -prices[1]);
sell[1] = Math.max(0, prices[1] - prices[0]);
for (int i = 2; i < N; i++) {
buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]);
sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);
}
return sell[N - 1];
}
// 最优解就是方法2的空间优化而已
public static int maxProfit3(int[] prices) {
if (prices.length < 2) {
return 0;
}
int buy1 = Math.max(-prices[0], -prices[1]);
int sell1 = Math.max(0, prices[1] - prices[0]);
int sell2 = 0;
for (int i = 2; i < prices.length; i++) {
int tmp = sell1;
sell1 = Math.max(sell1, buy1 + prices[i]);
buy1 = Math.max(buy1, sell2 - prices[i]);
sell2 = tmp;
}
return sell1;
}
}

@ -0,0 +1,32 @@
package 01_test;
import java.util.Arrays;
// 给定一个正数数组arr代表若干人的体重
// 再给定一个正数limit表示所有船共同拥有的载重量
// 每艘船最多坐两人,且不能超过载重
// 想让所有的人同时过河,并且用最好的分配方法让船尽量少
// 返回最少的船数
// 测试链接 : https://leetcode.com/problems/boats-to-save-people/
public class Code05_BoatsToSavePeople {
public static int numRescueBoats(int[] people, int limit) {
Arrays.sort(people);
int ans = 0;
int l = 0;
int r = people.length - 1;
int sum = 0;
while (l <= r) {
sum = l == r ? people[l] : people[l] + people[r];
if (sum > limit) {
r--;
} else {
l++;
r--;
}
ans++;
}
return ans;
}
}

@ -0,0 +1,50 @@
package 01_test;
import java.util.HashMap;
// 本题测试链接 : https://leetcode.com/problems/distinct-subsequences-ii/
public class Code05_DistinctSubseqValue {
public static int distinctSubseqII(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int m = 1000000007;
char[] str = s.toCharArray();
int[] count = new int[26];
int all = 1; // 算空集
for (char x : str) {
int add = (all - count[x - 'a'] + m) % m;
all = (all + add) % m;
count[x - 'a'] = (count[x - 'a'] + add) % m;
}
return all - 1;
}
public static int zuo(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int m = 1000000007;
char[] str = s.toCharArray();
HashMap<Character, Integer> map = new HashMap<>();
int all = 1; // 一个字符也没遍历的时候,有空集
for (char x : str) {
int newAdd = all;
// int curAll = all + newAdd - (map.containsKey(x) ? map.get(x) : 0);
int curAll = all;
curAll = (curAll + newAdd) % m;
curAll = (curAll - (map.containsKey(x) ? map.get(x) : 0) + m) % m;
all = curAll;
map.put(x, newAdd);
}
return all;
}
public static void main(String[] args) {
String s = "bccaccbaabbc";
System.out.println(distinctSubseqII(s) + 1);
System.out.println(zuo(s));
}
}

@ -0,0 +1,54 @@
package 01_test;
public class Code05_LongestIncreasingPath {
public static int longestIncreasingPath1(int[][] matrix) {
int ans = 0;
int N = matrix.length;
int M = matrix[0].length;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
ans = Math.max(ans, process1(matrix, i, j));
}
}
return ans;
}
// 从m[i][j]开始走,走出来的最长递增链,返回!
public static int process1(int[][] m, int i, int j) {
int up = i > 0 && m[i][j] < m[i - 1][j] ? process1(m, i - 1, j) : 0;
int down = i < (m.length - 1) && m[i][j] < m[i + 1][j] ? process1(m, i + 1, j) : 0;
int left = j > 0 && m[i][j] < m[i][j - 1] ? process1(m, i, j - 1) : 0;
int right = j < (m[0].length - 1) && m[i][j] < m[i][j + 1] ? process1(m, i, j + 1) : 0;
return Math.max(Math.max(up, down), Math.max(left, right)) + 1;
}
public static int longestIncreasingPath2(int[][] matrix) {
int ans = 0;
int N = matrix.length;
int M = matrix[0].length;
int[][] dp = new int[N][M];
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
ans = Math.max(ans, process2(matrix, i, j, dp));
}
}
return ans;
}
// 从m[i][j]开始走,走出来的最长递增链,返回!
public static int process2(int[][] m, int i, int j, int[][] dp) {
if (dp[i][j] != 0) {
return dp[i][j];
}
// (i,j)不越界
int up = i > 0 && m[i][j] < m[i - 1][j] ? process2(m, i - 1, j, dp) : 0;
int down = i < (m.length - 1) && m[i][j] < m[i + 1][j] ? process2(m, i + 1, j, dp) : 0;
int left = j > 0 && m[i][j] < m[i][j - 1] ? process2(m, i, j - 1, dp) : 0;
int right = j < (m[0].length - 1) && m[i][j] < m[i][j + 1] ? process2(m, i, j + 1, dp) : 0;
int ans = Math.max(Math.max(up, down), Math.max(left, right)) + 1;
dp[i][j] = ans;
return ans;
}
}

@ -0,0 +1,80 @@
package 01_test;
public class Code05_MinWindowLength {
public static int minLength(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() < s2.length()) {
return Integer.MAX_VALUE;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int[] map = new int[256]; // map[37] = 4 37 4次
for (int i = 0; i != str2.length; i++) {
map[str2[i]]++;
}
int all = str2.length;
// [L,R-1] R
// [L,R) -> [0,0)
int L = 0;
int R = 0;
int minLen = Integer.MAX_VALUE;
while (R != str1.length) {
map[str1[R]]--;
if (map[str1[R]] >= 0) {
all--;
}
if (all == 0) { // 还完了
while (map[str1[L]] < 0) {
map[str1[L++]]++;
}
// [L..R]
minLen = Math.min(minLen, R - L + 1);
all++;
map[str1[L++]]++;
}
R++;
}
return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
// 测试链接 : https://leetcode.com/problems/minimum-window-substring/
public static String minWindow(String s, String t) {
if (s.length() < t.length()) {
return "";
}
char[] str = s.toCharArray();
char[] target = t.toCharArray();
int[] map = new int[256];
for (char cha : target) {
map[cha]++;
}
int all = target.length;
int L = 0;
int R = 0;
int minLen = Integer.MAX_VALUE;
int ansl = -1;
int ansr = -1;
while (R != str.length) {
map[str[R]]--;
if (map[str[R]] >= 0) {
all--;
}
if (all == 0) {
while (map[str[L]] < 0) {
map[str[L++]]++;
}
if (minLen > R - L + 1) {
minLen = R - L + 1;
ansl = L;
ansr = R;
}
all++;
map[str[L++]]++;
}
R++;
}
return minLen == Integer.MAX_VALUE ? "" : s.substring(ansl, ansr + 1);
}
}

@ -0,0 +1,237 @@
package 01_test;
import java.util.HashSet;
public class Code05_WorldBreak {
/*
*
* . str. arr, 使.
* 使arrstr. .
*
*/
public static int ways(String str, String[] arr) {
HashSet<String> set = new HashSet<>();
for (String candidate : arr) {
set.add(candidate);
}
return process(str, 0, set);
}
// 所有的可分解字符串都已经放在了set中
// str[i....] 能够被set中的贴纸分解的话返回分解的方法数
public static int process(String str, int i, HashSet<String> set) {
if (i == str.length()) { // 没字符串需要分解了!
return 1;
}
// i....还有字符串需要分解
int ways = 0;
// [i ... end] 前缀串 每一个前缀串
for (int end = i; end < str.length(); end++) {
String pre = str.substring(i, end + 1);// [)
if (set.contains(pre)) {
ways += process(str, end + 1, set);
}
}
return ways;
}
public static int ways1(String str, String[] arr) {
if (str == null || str.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
HashSet<String> map = new HashSet<>();
for (String s : arr) {
map.add(s);
}
return f(str, map, 0);
}
public static int f(String str, HashSet<String> map, int index) {
if (index == str.length()) {
return 1;
}
int ways = 0;
for (int end = index; end < str.length(); end++) {
if (map.contains(str.substring(index, end + 1))) {
ways += f(str, map, end + 1);
}
}
return ways;
}
public static int ways2(String str, String[] arr) {
if (str == null || str.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
HashSet<String> map = new HashSet<>();
for (String s : arr) {
map.add(s);
}
int N = str.length();
int[] dp = new int[N + 1];
dp[N] = 1;
for (int i = N - 1; i >= 0; i--) {
for (int end = i; end < N; end++) {
if (map.contains(str.substring(i, end + 1))) {
dp[i] += dp[end + 1];
}
}
}
return dp[0];
}
public static class Node {
public boolean end;
public Node[] nexts;
public Node() {
end = false;
nexts = new Node[26];
}
}
public static int ways3(String str, String[] arr) {
if (str == null || str.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
Node root = new Node();
for (String s : arr) {
char[] chs = s.toCharArray();
Node node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
node.nexts[index] = new Node();
}
node = node.nexts[index];
}
node.end = true;
}
return g(str.toCharArray(), root, 0);
}
// str[i...] 被分解的方法数,返回
public static int g(char[] str, Node root, int i) {
if (i == str.length) {
return 1;
}
int ways = 0;
Node cur = root;
// i...end
for (int end = i; end < str.length; end++) {
int path = str[end] - 'a';
if (cur.nexts[path] == null) {
break;
}
cur = cur.nexts[path];
if (cur.end) { // i...end
ways += g(str, root, end + 1);
}
}
return ways;
}
public static int ways4(String s, String[] arr) {
if (s == null || s.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
Node root = new Node();
for (String str : arr) {
char[] chs = str.toCharArray();
Node node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
node.nexts[index] = new Node();
}
node = node.nexts[index];
}
node.end = true;
}
char[] str = s.toCharArray();
int N = str.length;
int[] dp = new int[N + 1];
dp[N] = 1;
for (int i = N - 1; i >= 0; i--) {
Node cur = root;
for (int end = i; end < N; end++) {
int path = str[end] - 'a';
if (cur.nexts[path] == null) {
break;
}
cur = cur.nexts[path];
if (cur.end) {
dp[i] += dp[end + 1];
}
}
}
return dp[0];
}
// 以下的逻辑都是为了测试
public static class RandomSample {
public String str;
public String[] arr;
public RandomSample(String s, String[] a) {
str = s;
arr = a;
}
}
// 随机样本产生器
public static RandomSample generateRandomSample(char[] candidates, int num, int len, int joint) {
String[] seeds = randomSeeds(candidates, num, len);
HashSet<String> set = new HashSet<>();
for (String str : seeds) {
set.add(str);
}
String[] arr = new String[set.size()];
int index = 0;
for (String str : set) {
arr[index++] = str;
}
StringBuilder all = new StringBuilder();
for (int i = 0; i < joint; i++) {
all.append(arr[(int) (Math.random() * arr.length)]);
}
return new RandomSample(all.toString(), arr);
}
public static String[] randomSeeds(char[] candidates, int num, int len) {
String[] arr = new String[(int) (Math.random() * num) + 1];
for (int i = 0; i < arr.length; i++) {
char[] str = new char[(int) (Math.random() * len) + 1];
for (int j = 0; j < str.length; j++) {
str[j] = candidates[(int) (Math.random() * candidates.length)];
}
arr[i] = String.valueOf(str);
}
return arr;
}
public static void main(String[] args) {
char[] candidates = { 'a', 'b' };
int num = 20;
int len = 4;
int joint = 5;
int testTimes = 30000;
boolean testResult = true;
for (int i = 0; i < testTimes; i++) {
RandomSample sample = generateRandomSample(candidates, num, len, joint);
int ans1 = ways1(sample.str, sample.arr);
int ans2 = ways2(sample.str, sample.arr);
int ans3 = ways3(sample.str, sample.arr);
int ans4 = ways4(sample.str, sample.arr);
if (ans1 != ans2 || ans3 != ans4 || ans2 != ans4) {
testResult = false;
}
}
System.out.println(testTimes + "次随机测试是否通过:" + testResult);
}
}

@ -0,0 +1,27 @@
package 01_test;
//leetcode 714
public class Code06_BestTimeToBuyAndSellStockWithTransactionFee {
public static int maxProfit(int[] arr, int fee) {
if (arr == null || arr.length < 2) {
return 0;
}
int N = arr.length;
// 0..0 0 -[0] - fee
int bestbuy = -arr[0] - fee;
// 0..0 卖 0
int bestsell = 0;
for (int i = 1; i < N; i++) {
// 来到i位置了
// 如果在i必须买 收入 - 批发价 - fee
int curbuy = bestsell - arr[i] - fee;
// 如果在i必须卖 整体最优(收入 - 良好批发价 - fee
int cursell = bestbuy + arr[i];
bestbuy = Math.max(bestbuy, curbuy);
bestsell = Math.max(bestsell, cursell);
}
return bestsell;
}
}

@ -0,0 +1,46 @@
package 01_test;
import java.util.Arrays;
// 本题测试链接 : https://leetcode.com/problems/closest-subsequence-sum/
// 本题数据量描述:
// 1 <= nums.length <= 40
// -10^7 <= nums[i] <= 10^7
// -10^9 <= goal <= 10^9
// 通过这个数据量描述可知,需要用到分治,因为数组长度不大
// 而值很大,用动态规划的话,表会爆
public class Code06_ClosestSubsequenceSum {
public static int[] l = new int[1 << 20];
public static int[] r = new int[1 << 20];
public static int minAbsDifference(int[] nums, int goal) {
if (nums == null || nums.length == 0) {
return goal;
}
int le = process(nums, 0, nums.length >> 1, 0, 0, l);
int re = process(nums, nums.length >> 1, nums.length, 0, 0, r);
Arrays.sort(l, 0, le);
Arrays.sort(r, 0, re--);
int ans = Math.abs(goal);
for (int i = 0; i < le; i++) {
int rest = goal - l[i];
while (re > 0 && Math.abs(rest - r[re - 1]) <= Math.abs(rest - r[re])) {
re--;
}
ans = Math.min(ans, Math.abs(rest - r[re]));
}
return ans;
}
public static int process(int[] nums, int index, int end, int sum, int fill, int[] arr) {
if (index == end) {
arr[fill++] = sum;
} else {
fill = process(nums, index + 1, end, sum, fill, arr);
fill = process(nums, index + 1, end, sum + nums[index], fill, arr);
}
return fill;
}
}

@ -0,0 +1,30 @@
package 01_test;
// 本题测试链接 : https://leetcode.com/problems/shortest-unsorted-continuous-subarray/
public class Code07_MinLengthForSort {
public static int findUnsortedSubarray(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
int N = nums.length;
int right = -1;
int max = Integer.MIN_VALUE;
for (int i = 0; i < N; i++) {
if (max > nums[i]) {
right = i;
}
max = Math.max(max, nums[i]);
}
int min = Integer.MAX_VALUE;
int left = N;
for (int i = N - 1; i >= 0; i--) {
if (min < nums[i]) {
left = i;
}
min = Math.min(min, nums[i]);
}
return Math.max(0, right - left + 1);
}
}

@ -0,0 +1,169 @@
package 01_test;
public class Code07_MoneyProblem {
// int[] d d[i]i号怪兽的武力
// int[] p p[i]i号怪兽要求的钱
// ability 当前你所具有的能力
// index 来到了第index个怪兽的面前
// 目前你的能力是ability你来到了index号怪兽的面前如果要通过后续所有的怪兽
// 请返回需要花的最少钱数
public static long process1(int[] d, int[] p, int ability, int index) {
if (index == d.length) {
return 0;
}
if (ability < d[index]) {
return p[index] + process1(d, p, ability + d[index], index + 1);
} else { // ability >= d[index] 可以贿赂,也可以不贿赂
return Math.min(
p[index] + process1(d, p, ability + d[index], index + 1),
0 + process1(d, p, ability, index + 1));
}
}
public static long func1(int[] d, int[] p) {
return process1(d, p, 0, 0);
}
// 从0....index号怪兽花的钱必须严格==money
// 如果通过不了,返回-1
// 如果可以通过,返回能通过情况下的最大能力值
public static long process2(int[] d, int[] p, int index, int money) {
if (index == -1) { // 一个怪兽也没遇到呢
return money == 0 ? 0 : -1;
}
// index >= 0
// 1) 不贿赂当前index号怪兽
long preMaxAbility = process2(d, p, index - 1, money);
long p1 = -1;
if (preMaxAbility != -1 && preMaxAbility >= d[index]) {
p1 = preMaxAbility;
}
// 2) 贿赂当前的怪兽 当前的钱 p[index]
long preMaxAbility2 = process2(d, p, index - 1, money - p[index]);
long p2 = -1;
if (preMaxAbility2 != -1) {
p2 = d[index] + preMaxAbility2;
}
return Math.max(p1, p2);
}
public static int minMoney2(int[] d, int[] p) {
int allMoney = 0;
for (int i = 0; i < p.length; i++) {
allMoney += p[i];
}
int N = d.length;
for (int money = 0; money < allMoney; money++) {
if (process2(d, p, N - 1, money) != -1) {
return money;
}
}
return allMoney;
}
public static long func2(int[] d, int[] p) {
int sum = 0;
for (int num : d) {
sum += num;
}
long[][] dp = new long[d.length + 1][sum + 1];
for (int i = 0; i <= sum; i++) {
dp[0][i] = 0;
}
for (int cur = d.length - 1; cur >= 0; cur--) {
for (int hp = 0; hp <= sum; hp++) {
// 如果这种情况发生那么这个hp必然是递归过程中不会出现的状态
// 既然动态规划是尝试过程的优化,尝试过程碰不到的状态,不必计算
if (hp + d[cur] > sum) {
continue;
}
if (hp < d[cur]) {
dp[cur][hp] = p[cur] + dp[cur + 1][hp + d[cur]];
} else {
dp[cur][hp] = Math.min(p[cur] + dp[cur + 1][hp + d[cur]], dp[cur + 1][hp]);
}
}
}
return dp[0][0];
}
public static long func3(int[] d, int[] p) {
int sum = 0;
for (int num : p) {
sum += num;
}
// dp[i][j]含义:
// 能经过0i的怪兽且花钱为j花钱的严格等于j时的武力值最大是多少
// 如果dp[i][j]==-1表示经过0i的怪兽花钱为j是无法通过的或者之前的钱怎么组合也得不到正好为j的钱数
int[][] dp = new int[d.length][sum + 1];
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j <= sum; j++) {
dp[i][j] = -1;
}
}
// 经过0i的怪兽花钱数一定为p[0]达到武力值d[0]的地步。其他第0行的状态一律是无效的
dp[0][p[0]] = d[0];
for (int i = 1; i < d.length; i++) {
for (int j = 0; j <= sum; j++) {
// 可能性一,为当前怪兽花钱
// 存在条件:
// j - p[i]要不越界并且在钱数为j - p[i]时要能通过0i-1的怪兽并且钱数组合是有效的。
if (j >= p[i] && dp[i - 1][j - p[i]] != -1) {
dp[i][j] = dp[i - 1][j - p[i]] + d[i];
}
// 可能性二,不为当前怪兽花钱
// 存在条件:
// 0~i-1怪兽在花钱为j的情况下能保证通过当前i位置的怪兽
if (dp[i - 1][j] >= d[i]) {
// 两种可能性中,选武力值最大的
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
}
}
}
int ans = 0;
// dp表最后一行上dp[N-1][j]代表:
// 能经过0N-1的怪兽且花钱为j花钱的严格等于j时的武力值最大是多少
// 那么最后一行上,最左侧的不为-1的列数(j),就是答案
for (int j = 0; j <= sum; j++) {
if (dp[d.length - 1][j] != -1) {
ans = j;
break;
}
}
return ans;
}
public static int[][] generateTwoRandomArray(int len, int value) {
int size = (int) (Math.random() * len) + 1;
int[][] arrs = new int[2][size];
for (int i = 0; i < size; i++) {
arrs[0][i] = (int) (Math.random() * value) + 1;
arrs[1][i] = (int) (Math.random() * value) + 1;
}
return arrs;
}
public static void main(String[] args) {
int len = 10;
int value = 20;
int testTimes = 10000;
for (int i = 0; i < testTimes; i++) {
int[][] arrs = generateTwoRandomArray(len, value);
int[] d = arrs[0];
int[] p = arrs[1];
long ans1 = func1(d, p);
long ans2 = func2(d, p);
long ans3 = func3(d, p);
long ans4 = minMoney2(d,p);
if (ans1 != ans2 || ans2 != ans3 || ans1 != ans4) {
System.out.println("oops!");
}
}
}
}

@ -0,0 +1,127 @@
package 01_test;
import java.util.HashMap;
// leetcode 494题
public class Code07_TargetSum {
public static int findTargetSumWays1(int[] arr, int s) {
return process1(arr, 0, s);
}
// 可以自由使用arr[index....]所有的数字!
// 搞出rest这个数方法数是多少返回
// index == 7 rest = 13
// map "7_13" 256
public static int process1(int[] arr, int index, int rest) {
if (index == arr.length) { // 没数了!
return rest == 0 ? 1 : 0;
}
// 还有数arr[index] arr[index+1 ... ]
return process1(arr, index + 1, rest - arr[index]) + process1(arr, index + 1, rest + arr[index]);
}
public static int findTargetSumWays2(int[] arr, int s) {
return process2(arr, 0, s, new HashMap<>());
}
public static int process2(int[] arr, int index, int rest, HashMap<Integer, HashMap<Integer, Integer>> dp) {
if (dp.containsKey(index) && dp.get(index).containsKey(rest)) {
return dp.get(index).get(rest);
}
// 否则,没命中!
int ans = 0;
if (index == arr.length) {
ans = rest == 0 ? 1 : 0;
} else {
ans = process2(arr, index + 1, rest - arr[index], dp) + process2(arr, index + 1, rest + arr[index], dp);
}
if (!dp.containsKey(index)) {
dp.put(index, new HashMap<>());
}
dp.get(index).put(rest, ans);
return ans;
}
// 优化点一 :
// 你可以认为arr中都是非负数
// 因为即便是arr中有负数比如[3,-4,2]
// 因为你能在每个数前面用+或者-号
// 所以[3,-4,2]其实和[3,4,2]达成一样的效果
// 那么我们就全把arr变成非负数不会影响结果的
// 优化点二 :
// 如果arr都是非负数并且所有数的累加和是sum
// 那么如果target<sum很明显没有任何方法可以达到target可以直接返回0
// 优化点三 :
// arr内部的数组不管怎么+和-,最终的结果都一定不会改变奇偶性
// 所以如果所有数的累加和是sum
// 并且与target的奇偶性不一样没有任何方法可以达到target可以直接返回0
// 优化点四 :
// 比如说给定一个数组, arr = [1, 2, 3, 4, 5] 并且 target = 3
// 其中一个方案是 : +1 -2 +3 -4 +5 = 3
// 该方案中取了正的集合为P = {135}
// 该方案中取了负的集合为N = {24}
// 所以任何一种方案,都一定有 sum(P) - sum(N) = target
// 现在我们来处理一下这个等式把左右两边都加上sum(P) + sum(N),那么就会变成如下:
// sum(P) - sum(N) + sum(P) + sum(N) = target + sum(P) + sum(N)
// 2 * sum(P) = target + 数组所有数的累加和
// sum(P) = (target + 数组所有数的累加和) / 2
// 也就是说,任何一个集合,只要累加和是(target + 数组所有数的累加和) / 2
// 那么就一定对应一种target的方式
// 也就是说比如非负数组arrtarget = 7, 而所有数累加和是11
// 求有多少方法组成7其实就是求有多少种达到累加和(7+11)/2=9的方法
// 优化点五 :
// 二维动态规划的空间压缩技巧
public static int findTargetSumWays(int[] arr, int target) {
int sum = 0;
for (int n : arr) {
sum += n;
}
return sum < target || ((target & 1) ^ (sum & 1)) != 0 ? 0 : subset2(arr, (target + sum) >> 1);
}
// 求非负数组nums有多少个子集累加和是s
// 二维动态规划
// 不用空间压缩
public static int subset1(int[] nums, int s) {
if (s < 0) {
return 0;
}
int n = nums.length;
// dp[i][j] : nums前缀长度为i的所有子集有多少累加和是j
int[][] dp = new int[n + 1][s + 1];
// nums前缀长度为0的所有子集有多少累加和是0一个空集
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= s; j++) {
dp[i][j] = dp[i - 1][j];
if (j - nums[i - 1] >= 0) {
dp[i][j] += dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[n][s];
}
// 求非负数组nums有多少个子集累加和是s
// 二维动态规划
// 用空间压缩:
// 核心就是for循环里面的for (int i = s; i >= n; i--) {
// 为啥不枚举所有可能的累加和?只枚举 n...s 这些累加和?
// 因为如果 i - n < 0dp[i]怎么更新和上一步的dp[i]一样!所以不用更新
// 如果 i - n >= 0dp[i]怎么更新上一步的dp[i] + 上一步dp[i - n]的值,这才需要更新
public static int subset2(int[] nums, int s) {
if (s < 0) {
return 0;
}
int[] dp = new int[s + 1];
dp[0] = 1;
for (int n : nums) {
for (int i = s; i >= n; i--) {
dp[i] += dp[i - n];
}
}
return dp[s];
}
}

@ -0,0 +1,63 @@
package 01_test;
// 来自哈喽单车
// 本题是leetcode原题 : https://leetcode.com/problems/stone-game-iv/
public class Code11_StoneGameIV {
// 当前的!先手,会不会赢
// 打表,不能发现规律
public static boolean winnerSquareGame1(int n) {
if (n == 0) {
return false;
}
// 当前的先手会尝试所有的情况149162536....
for (int i = 1; i * i <= n; i++) {
// 当前的先手,决定拿走 i * i 这个平方数
// 它的对手会不会赢? winnerSquareGame1(n - i * i)
if (!winnerSquareGame1(n - i * i)) {
return true;
}
}
return false;
}
public static boolean winnerSquareGame2(int n) {
int[] dp = new int[n + 1];
dp[0] = -1;
return process2(n, dp);
}
public static boolean process2(int n, int[] dp) {
if (dp[n] != 0) {
return dp[n] == 1 ? true : false;
}
boolean ans = false;
for (int i = 1; i * i <= n; i++) {
if (!process2(n - i * i, dp)) {
ans = true;
break;
}
}
dp[n] = ans ? 1 : -1;
return ans;
}
public static boolean winnerSquareGame3(int n) {
boolean[] dp = new boolean[n + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j * j <= i; j++) {
if (!dp[i - j * j]) {
dp[i] = true;
break;
}
}
}
return dp[n];
}
public static void main(String[] args) {
int n = 10000000;
System.out.println(winnerSquareGame3(n));
}
}

@ -0,0 +1,45 @@
package 01_test;
public class Problem_0152_MaximumProductSubarray {
public static double max(double[] arr) {
if(arr == null || arr.length == 0) {
return 0; // 报错!
}
int n = arr.length;
// 上一步的最大
double premax = arr[0];
// 上一步的最小
double premin = arr[0];
double ans = arr[0];
for(int i = 1; i < n; i++) {
double p1 = arr[i];
double p2 = arr[i] * premax;
double p3 = arr[i] * premin;
double curmax = Math.max(Math.max(p1, p2), p3);
double curmin = Math.min(Math.min(p1, p2), p3);
ans = Math.max(ans, curmax);
premax = curmax;
premin = curmin;
}
return ans;
}
public static int maxProduct(int[] nums) {
int ans = nums[0];
int min = nums[0];
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
int curmin = Math.min(nums[i], Math.min(min * nums[i], max * nums[i]));
int curmax = Math.max(nums[i], Math.max(min * nums[i], max * nums[i]));
min = curmin;
max = curmax;
ans = Math.max(ans, max);
}
return ans;
}
}

@ -0,0 +1,50 @@
package 01_test;
public class Problem_0213_HouseRobberII {
// arr 长度大于等于1
public static int pickMaxSum(int[] arr) {
int n = arr.length;
// dp[i] : arr[0..i]范围上,随意选择,但是,任何两数不能相邻。得到的最大累加和是多少?
int[] dp = new int[n];
dp[0] = arr[0];
dp[1] = Math.max(arr[0], arr[1]);
for (int i = 2; i < n; i++) {
int p1 = arr[i];
int p2 = dp[i - 1];
int p3 = arr[i] + dp[i - 2];
dp[i] = Math.max(p1, Math.max(p2, p3));
}
return dp[n - 1];
}
public static int rob(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
if (nums.length == 1) {
return nums[0];
}
if (nums.length == 2) {
return Math.max(nums[0], nums[1]);
}
int pre2 = nums[0];
int pre1 = Math.max(nums[0], nums[1]);
for (int i = 2; i < nums.length - 1; i++) {
int tmp = Math.max(pre1, nums[i] + pre2);
pre2 = pre1;
pre1 = tmp;
}
int ans1 = pre1;
pre2 = nums[1];
pre1 = Math.max(nums[1], nums[2]);
for (int i = 3; i < nums.length; i++) {
int tmp = Math.max(pre1, nums[i] + pre2);
pre2 = pre1;
pre1 = tmp;
}
int ans2 = pre1;
return Math.max(ans1, ans2);
}
}

@ -0,0 +1,56 @@
package 01_test;
public class Problem_0265_PaintHouseII {
// costs[i][k] i号房子用k颜色刷的花费
// 要让0...N-1的房子相邻不同色
// 返回最小花费
public static int minCostII(int[][] costs) {
int N = costs.length;
if (N == 0) {
return 0;
}
int K = costs[0].length;
// 之前取得的最小代价、取得最小代价时的颜色
int preMin1 = 0;
int preEnd1 = -1;
// 之前取得的次小代价、取得次小代价时的颜色
int preMin2 = 0;
int preEnd2 = -1;
for (int i = 0; i < N; i++) { // i房子
int curMin1 = Integer.MAX_VALUE;
int curEnd1 = -1;
int curMin2 = Integer.MAX_VALUE;
int curEnd2 = -1;
for (int j = 0; j < K; j++) { // j颜色
if (j != preEnd1) {
if (preMin1 + costs[i][j] < curMin1) {
curMin2 = curMin1;
curEnd2 = curEnd1;
curMin1 = preMin1 + costs[i][j];
curEnd1 = j;
} else if (preMin1 + costs[i][j] < curMin2) {
curMin2 = preMin1 + costs[i][j];
curEnd2 = j;
}
} else if (j != preEnd2) {
if (preMin2 + costs[i][j] < curMin1) {
curMin2 = curMin1;
curEnd2 = curEnd1;
curMin1 = preMin2 + costs[i][j];
curEnd1 = j;
} else if (preMin2 + costs[i][j] < curMin2) {
curMin2 = preMin2 + costs[i][j];
curEnd2 = j;
}
}
}
preMin1 = curMin1;
preEnd1 = curEnd1;
preMin2 = curMin2;
preEnd2 = curEnd2;
}
return preMin1;
}
}

@ -0,0 +1,110 @@
package 01_test;
public class Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters {
public static int longestSubstring1(String s, int k) {
char[] str = s.toCharArray();
int N = str.length;
int max = 0;
for (int i = 0; i < N; i++) {
int[] count = new int[256];
int collect = 0;
int satisfy = 0;
for (int j = i; j < N; j++) {
if (count[str[j]] == 0) {
collect++;
}
if (count[str[j]] == k - 1) {
satisfy++;
}
count[str[j]]++;
if (collect == satisfy) {
max = Math.max(max, j - i + 1);
}
}
}
return max;
}
public static int longestSubstring2(String s, int k) {
char[] str = s.toCharArray();
int N = str.length;
int max = 0;
for (int require = 1; require <= 26; require++) {
// 3种
// a~z 出现次数
int[] count = new int[26];
// 目前窗口内收集了几种字符了
int collect = 0;
// 目前窗口内出现次数>=k次的字符满足了几种
int satisfy = 0;
// 窗口右边界
int R = -1;
for (int L = 0; L < N; L++) { // L要尝试每一个窗口的最左位置
// [L..R] R+1
while (R + 1 < N && !(collect == require && count[str[R + 1] - 'a'] == 0)) {
R++;
if (count[str[R] - 'a'] == 0) {
collect++;
}
if (count[str[R] - 'a'] == k - 1) {
satisfy++;
}
count[str[R] - 'a']++;
}
// [L...R]
if (satisfy == require) {
max = Math.max(max, R - L + 1);
}
// L++
if (count[str[L] - 'a'] == 1) {
collect--;
}
if (count[str[L] - 'a'] == k) {
satisfy--;
}
count[str[L] - 'a']--;
}
}
return max;
}
// 会超时,但是思路的确是正确的
public static int longestSubstring3(String s, int k) {
return process(s.toCharArray(), 0, s.length() - 1, k);
}
public static int process(char[] str, int L, int R, int k) {
if (L > R) {
return 0;
}
int[] counts = new int[26];
for (int i = L; i <= R; i++) {
counts[str[i] - 'a']++;
}
char few = 0;
int min = Integer.MAX_VALUE;
for (int i = 0; i < 26; i++) {
if (counts[i] != 0 && min > counts[i]) {
few = (char) (i + 'a');
min = counts[i];
}
}
if (min >= k) {
return R - L + 1;
}
int pre = 0;
int max = Integer.MIN_VALUE;
for (int i = L; i <= R; i++) {
if (str[i] == few) {
max = Math.max(max, process(str, pre, i - 1, k));
pre = i + 1;
}
}
if (pre != R + 1) {
max = Math.max(max, process(str, pre, R, k));
}
return max;
}
}

@ -0,0 +1,81 @@
package 01_test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.PriorityQueue;
public class Problem_1488_AvoidFloodInTheCity {
// rains[i] = j 第i天轮到j号湖泊下雨
// 规定,下雨日,干啥 : -1
// 不下雨日,如果没有湖泊可抽 : 1
public static int[] avoidFlood(int[] rains) {
int n = rains.length;
int[] ans = new int[n];
int[] invalid = new int[0];
// key : 某个湖
// value : 这个湖在哪些位置降雨
// 4 : {3,7,19,21}
// 1 : { 13 }
// 2 : {4, 56}
HashMap<Integer, LinkedList<Integer>> map = new HashMap<>();
for (int i = 0; i < n; i++) {
if (rains[i] != 0) { // 第i天要下雨rains[i]
// 3天 9号
// 9号 { 3 }
// 9号 {1, 3}
if (!map.containsKey(rains[i])) {
map.put(rains[i], new LinkedList<>());
}
map.get(rains[i]).addLast(i);
}
}
// 没抽干的湖泊表
// 某个湖如果满了加入到set里
// 某个湖被抽干了从set中移除
HashSet<Integer> set = new HashSet<>();
// 这个堆的堆顶表示最先处理的湖是哪个
PriorityQueue<Work> heap = new PriorityQueue<>();
for (int i = 0; i < n; i++) { // 0 1 2 3 ...
if (rains[i] != 0) {
if (set.contains(rains[i])) {
return invalid;
}
// 放入到没抽干的表里
set.add(rains[i]);
map.get(rains[i]).pollFirst();
if (!map.get(rains[i]).isEmpty()) {
heap.add(new Work(rains[i], map.get(rains[i]).peekFirst()));
}
// 题目规定
ans[i] = -1;
} else { // 今天干活!
if (heap.isEmpty()) {
ans[i] = 1;
} else {
Work cur = heap.poll();
set.remove(cur.lake);
ans[i] = cur.lake;
}
}
}
return ans;
}
public static class Work implements Comparable<Work> {
public int lake;
public int nextRain;
public Work(int l, int p) {
lake = l;
nextRain = p;
}
@Override
public int compareTo(Work o) {
return nextRain - o.nextRain;
}
}
}
Loading…
Cancel
Save