diff --git a/src/class32/Code01_IndexTree.java b/src/class32/Code01_IndexTree.java index f7d5eca..4c4bdab 100644 --- a/src/class32/Code01_IndexTree.java +++ b/src/class32/Code01_IndexTree.java @@ -2,16 +2,19 @@ package class32; public class Code01_IndexTree { + // 下标从1开始! public static class IndexTree { private int[] tree; private int N; + // 0位置弃而不用! public IndexTree(int size) { N = size; tree = new int[N + 1]; } + // 1~index 累加和是多少? public int sum(int index) { int ret = 0; while (index > 0) { @@ -21,6 +24,9 @@ public class Code01_IndexTree { return ret; } + // index & -index : 提取出index最右侧的1出来 + // index : 0011001000 + // index & -index : 0000001000 public void add(int index, int d) { while (index <= N) { tree[index] += d; diff --git a/src/class32/Code04_AC2.java b/src/class32/Code04_AC2.java index fc915bc..e684de3 100644 --- a/src/class32/Code04_AC2.java +++ b/src/class32/Code04_AC2.java @@ -17,6 +17,7 @@ public class Code04_AC2 { public boolean endUse; public Node fail; public Node[] nexts; + public Node() { endUse = false; end = null; @@ -39,8 +40,7 @@ public class Code04_AC2 { for (int i = 0; i < str.length; i++) { index = str[i] - 'a'; if (cur.nexts[index] == null) { - Node next = new Node(); - cur.nexts[index] = next; + cur.nexts[index] = new Node(); } cur = cur.nexts[index]; } @@ -53,14 +53,12 @@ public class Code04_AC2 { Node cur = null; Node cfail = null; while (!queue.isEmpty()) { - // 当前节点弹出, - // 当前节点的所有后代加入到队列里去, - // 当前节点给它的子去设置fail指针 - // cur -> 父亲 + // 某个父亲,cur cur = queue.poll(); for (int i = 0; i < 26; i++) { // 所有的路 - if (cur.nexts[i] != null) { // 找到所有有效的路 - cur.nexts[i].fail = root; // + // cur -> 父亲 i号儿子,必须把i号儿子的fail指针设置好! + if (cur.nexts[i] != null) { // 如果真的有i号儿子 + cur.nexts[i].fail = root; cfail = cur.fail; while (cfail != null) { if (cfail.nexts[i] != null) { @@ -75,6 +73,7 @@ public class Code04_AC2 { } } + // 大文章:content public List containWords(String content) { char[] str = content.toCharArray(); Node cur = root; @@ -92,7 +91,7 @@ public class Code04_AC2 { cur = cur.nexts[index] != null ? cur.nexts[index] : root; follow = cur; while (follow != root) { - if(follow.endUse) { + if (follow.endUse) { break; } // 不同的需求,在这一段之间修改 @@ -115,8 +114,8 @@ public class Code04_AC2 { ac.insert("he"); ac.insert("abcdheks"); // 设置fail指针 - ac.build(); - + ac.build(); + List contains = ac.containWords("abcdhekskdjfafhasldkflskdjhwqaeruv"); for (String word : contains) { System.out.println(word); diff --git a/src/class33/Hash.java b/src/class33/Hash.java index 552191b..2c54b7d 100644 --- a/src/class33/Hash.java +++ b/src/class33/Hash.java @@ -29,7 +29,7 @@ public class Hash { } System.out.println("======="); - String algorithm = "SHA"; + String algorithm = "MD5"; Hash hash = new Hash(algorithm); String input1 = "zuochengyunzuochengyun1"; diff --git a/src/class37/Code01_CountofRangeSum.java b/src/class37/Code01_CountofRangeSum.java index e8f1292..d79fa02 100644 --- a/src/class37/Code01_CountofRangeSum.java +++ b/src/class37/Code01_CountofRangeSum.java @@ -164,18 +164,17 @@ public class Code01_CountofRangeSum { } public static int countRangeSum2(int[] nums, int lower, int upper) { + // 黑盒,加入数字(前缀和),不去重,可以接受重复数字 + // < num , 有几个数? SizeBalancedTreeSet treeSet = new SizeBalancedTreeSet(); long sum = 0; int ans = 0; treeSet.add(0);// 一个数都没有的时候,就已经有一个前缀和累加和为0, for (int i = 0; i < nums.length; i++) { sum += nums[i]; - // sum i结尾的时候[lower, upper] - // 之前所有前缀累加和中,有多少累加和落在[sum - upper, sum - lower] - // 查 ? < sum - lower + 1 a - // 查 ? < sum - upper b - // a - b - + // [sum - upper, sum - lower] + // [10, 20] ? + // < 10 ? < 21 ? long a = treeSet.lessKeySize(sum - lower + 1); long b = treeSet.lessKeySize(sum - upper); ans += a - b; diff --git a/src/class37/Code04_QueueReconstructionByHeight.java b/src/class37/Code04_QueueReconstructionByHeight.java new file mode 100644 index 0000000..2513402 --- /dev/null +++ b/src/class37/Code04_QueueReconstructionByHeight.java @@ -0,0 +1,265 @@ +package class37; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; + +// 本题测试链接:https://leetcode.com/problems/queue-reconstruction-by-height/ +public class Code04_QueueReconstructionByHeight { + + public static int[][] reconstructQueue1(int[][] people) { + int N = people.length; + Unit[] units = new Unit[N]; + for (int i = 0; i < N; i++) { + units[i] = new Unit(people[i][0], people[i][1]); + } + Arrays.sort(units, new UnitComparator()); + ArrayList arrList = new ArrayList<>(); + for (Unit unit : units) { + arrList.add(unit.k, unit); + } + int[][] ans = new int[N][2]; + int index = 0; + for (Unit unit : arrList) { + ans[index][0] = unit.h; + ans[index++][1] = unit.k; + } + return ans; + } + + public static int[][] reconstructQueue2(int[][] people) { + int N = people.length; + Unit[] units = new Unit[N]; + for (int i = 0; i < N; i++) { + units[i] = new Unit(people[i][0], people[i][1]); + } + Arrays.sort(units, new UnitComparator()); + SBTree tree = new SBTree(); + for (int i = 0; i < N; i++) { + tree.insert(units[i].k, i); + } + LinkedList allIndexes = tree.allIndexes(); + int[][] ans = new int[N][2]; + int index = 0; + for (Integer arri : allIndexes) { + ans[index][0] = units[arri].h; + ans[index++][1] = units[arri].k; + } + return ans; + } + + public static class Unit { + public int h; + public int k; + + public Unit(int height, int greater) { + h = height; + k = greater; + } + } + + public static class UnitComparator implements Comparator { + + @Override + public int compare(Unit o1, Unit o2) { + return o1.h != o2.h ? (o2.h - o1.h) : (o1.k - o2.k); + } + + } + + public static class SBTNode { + public int value; + public SBTNode l; + public SBTNode r; + public int size; + + public SBTNode(int arrIndex) { + value = arrIndex; + size = 1; + } + } + + public static class SBTree { + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + int leftSize = cur.l != null ? cur.l.size : 0; + int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + int rightSize = cur.r != null ? cur.r.size : 0; + int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode insert(SBTNode root, int index, SBTNode cur) { + if (root == null) { + return cur; + } + root.size++; + int leftAndHeadSize = (root.l != null ? root.l.size : 0) + 1; + if (index < leftAndHeadSize) { + root.l = insert(root.l, index, cur); + } else { + root.r = insert(root.r, index - leftAndHeadSize, cur); + } + root = maintain(root); + return root; + } + + private SBTNode get(SBTNode root, int index) { + int leftSize = root.l != null ? root.l.size : 0; + if (index < leftSize) { + return get(root.l, index); + } else if (index == leftSize) { + return root; + } else { + return get(root.r, index - leftSize - 1); + } + } + + private void process(SBTNode head, LinkedList indexes) { + if (head == null) { + return; + } + process(head.l, indexes); + indexes.addLast(head.value); + process(head.r, indexes); + } + + public void insert(int index, int value) { + SBTNode cur = new SBTNode(value); + if (root == null) { + root = cur; + } else { + if (index <= root.size) { + root = insert(root, index, cur); + } + } + } + + public int get(int index) { + SBTNode ans = get(root, index); + return ans.value; + } + + public LinkedList allIndexes() { + LinkedList indexes = new LinkedList<>(); + process(root, indexes); + return indexes; + } + + } + + // 通过以下这个测试, + // 可以很明显的看到LinkedList的插入和get效率不如SBTree + // LinkedList需要找到index所在的位置之后才能插入或者读取,时间复杂度O(N) + // SBTree是平衡搜索二叉树,所以插入或者读取时间复杂度都是O(logN) + public static void main(String[] args) { + // 功能测试 + int test = 10000; + int max = 1000000; + boolean pass = true; + LinkedList list = new LinkedList<>(); + SBTree sbtree = new SBTree(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + sbtree.insert(randomIndex, randomValue); + } + for (int i = 0; i < test; i++) { + if (list.get(i) != sbtree.get(i)) { + pass = false; + break; + } + } + System.out.println("功能测试是否通过 : " + pass); + + // 性能测试 + test = 50000; + list = new LinkedList<>(); + sbtree = new SBTree(); + long start = 0; + long end = 0; + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("LinkedList插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + list.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("LinkedList读取总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + sbtree.insert(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("SBTree插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + sbtree.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("SBTree读取总时长(毫秒) : " + (end - start)); + + } + +} diff --git a/src/class38/Code01_AppleMinBags.java b/src/class38/Code01_AppleMinBags.java new file mode 100644 index 0000000..54d4182 --- /dev/null +++ b/src/class38/Code01_AppleMinBags.java @@ -0,0 +1,47 @@ +package class38; + +public class Code01_AppleMinBags { + + public static int minBags(int apple) { + if (apple < 0) { + return -1; + } + int bag6 = -1; + int bag8 = apple / 8; + int rest = apple - 8 * bag8; + while (bag8 >= 0 && rest < 24) { + int restUse6 = minBagBase6(rest); + if (restUse6 != -1) { + bag6 = restUse6; + break; + } + rest = apple - 8 * (--bag8); + } + return bag6 == -1 ? -1 : bag6 + bag8; + } + + // 如果剩余苹果rest可以被装6个苹果的袋子搞定,返回袋子数量 + // 不能搞定返回-1 + public static int minBagBase6(int rest) { + return rest % 6 == 0 ? (rest / 6) : -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 < 100;apple++) { + System.out.println(apple + " : "+ minBags(apple)); + } + + } + +} diff --git a/src/class38/Code02_EatGrass.java b/src/class38/Code02_EatGrass.java new file mode 100644 index 0000000..200abcd --- /dev/null +++ b/src/class38/Code02_EatGrass.java @@ -0,0 +1,45 @@ +package class38; + +public class Code02_EatGrass { + + // n份青草放在一堆 + // 先手后手都绝顶聪明 + // string "先手" "后手" + public static String winner1(int n) { + // 0 1 2 3 4 + // 后 先 后 先 先 + if (n < 5) { // base case + return (n == 0 || n == 2) ? "后手" : "先手"; + } + // n >= 5 时 + int base = 1; // 当前先手决定吃的草数 + // 当前是先手在选 + while (base <= n) { + // 当前一共n份草,先手吃掉的是base份,n - base 是留给后手的草 + // 母过程 先手 在子过程里是 后手 + 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 + " : " + winner1(i)); + } + } + +} diff --git a/src/class38/Code03_MSumToN.java b/src/class38/Code03_MSumToN.java new file mode 100644 index 0000000..81e6bad --- /dev/null +++ b/src/class38/Code03_MSumToN.java @@ -0,0 +1,41 @@ +package class38; + +public class Code03_MSumToN { + + public static boolean isMSum1(int num) { + for (int i = 1; i <= num; i++) { + int sum = i; + for (int j = i + 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) { + if (num < 3) { + return false; + } + 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"); + + } +} diff --git a/src/class38/Code04_MoneyProblem.java b/src/class38/Code04_MoneyProblem.java new file mode 100644 index 0000000..0ea4e4a --- /dev/null +++ b/src/class38/Code04_MoneyProblem.java @@ -0,0 +1,131 @@ +package class38; + +public class Code04_MoneyProblem { + + // int[] d d[i]:i号怪兽的武力 + // int[] p p[i]:i号怪兽要求的钱 + // ability 当前你所具有的能力 + // index 来到了第index个怪兽的面前 + + // 目前,你的能力是ability,你来到了index号怪兽的面前,如果要通过后续所有的怪兽, + // 请返回需要花的最少钱数 + public static long process(int[] d, int[] p, int ability, int index) { + if (index == d.length) { + return 0; + } + if (ability < d[index]) { + return p[index] + process(d, p, ability + d[index], index + 1); + } else { // 可以贿赂,也可以不贿赂 + return + Math.min( + p[index] + process(d, p, ability + d[index], index + 1), + process(d, p, ability, index + 1) + ); + } + } + + public static long func1(int[] d, int[] p) { + return process(d, p, 0, 0); + } + + 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]含义: + // 能经过0~i的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少? + // 如果dp[i][j]==-1,表示经过0~i的怪兽,花钱为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; + } + } + // 经过0~i的怪兽,花钱数一定为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]时,要能通过0~i-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]代表: + // 能经过0~N-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 = 1000000; + 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); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("oops!"); + } + } + + } + +} diff --git a/src/class39/Code01_DifferentBTNum.java b/src/class39/Code01_DifferentBTNum.java new file mode 100644 index 0000000..4655e3c --- /dev/null +++ b/src/class39/Code01_DifferentBTNum.java @@ -0,0 +1,66 @@ +package class39; + +public class Code01_DifferentBTNum { + +// k(0) = 1, k(1) = 1 +// +// k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + ... + k(n - 2) * k(1) + k(n - 1) * k(0) +// 或者 +// k(n) = c(2n, n) / (n + 1) +// 或者 +// k(n) = c(2n, n) - c(2n, n-1) + + public static long num1(int N) { + if (N < 0) { + return 0; + } + if (N < 2) { + return 1; + } + long[] dp = new long[N + 1]; + dp[0] = 1; + dp[1] = 1; + for (int i = 2; i <= N; i++) { + for (int leftSize = 0; leftSize < i; leftSize++) { + dp[i] += dp[leftSize] * dp[i - 1 - leftSize]; + } + } + return dp[N]; + } + + public static long num2(int N) { + if (N < 0) { + return 0; + } + if (N < 2) { + return 1; + } + long a = 1; + long b = 1; + for (int i = 1, j = N + 1; i <= N; i++, j++) { + a *= i; + b *= j; + long gcd = gcd(a, b); + a /= gcd; + b /= gcd; + } + return (b / a) / (N + 1); + } + + public static long gcd(long m, long n) { + return n == 0 ? m : gcd(n, m % n); + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int i = 0; i < 15; i++) { + long ans1 = num1(i); + long ans2 = num2(i); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/src/class39/Code02_10Ways.java b/src/class39/Code02_10Ways.java new file mode 100644 index 0000000..1068ce2 --- /dev/null +++ b/src/class39/Code02_10Ways.java @@ -0,0 +1,92 @@ +package class39; + +import java.util.LinkedList; + +public class Code02_10Ways { + + public static long ways1(int N) { + int zero = N; + int one = N; + LinkedList path = new LinkedList<>(); + LinkedList> ans = new LinkedList<>(); + process(zero, one, path, ans); + long count = 0; + for (LinkedList cur : ans) { + int status = 0; + for (Integer num : cur) { + if (num == 0) { + status++; + } else { + status--; + } + if (status < 0) { + break; + } + } + if (status == 0) { + count++; + } + } + return count; + } + + public static void process(int zero, int one, LinkedList path, LinkedList> ans) { + if (zero == 0 && one == 0) { + LinkedList cur = new LinkedList<>(); + for (Integer num : path) { + cur.add(num); + } + ans.add(cur); + } else { + if (zero == 0) { + path.addLast(1); + process(zero, one - 1, path, ans); + path.removeLast(); + } else if (one == 0) { + path.addLast(0); + process(zero - 1, one, path, ans); + path.removeLast(); + } else { + path.addLast(1); + process(zero, one - 1, path, ans); + path.removeLast(); + path.addLast(0); + process(zero - 1, one, path, ans); + path.removeLast(); + } + } + } + + public static long ways2(int N) { + if (N < 0) { + return 0; + } + if (N < 2) { + return 1; + } + long a = 1; + long b = 1; + long limit = N << 1; + for (long i = 1; i <= limit; i++) { + if (i <= N) { + a *= i; + } else { + b *= i; + } + } + return (b / a) / (N + 1); + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int i = 0; i < 10; i++) { + long ans1 = ways1(i); + long ans2 = ways2(i); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/src/class39/Code03_RotateMatrix.java b/src/class39/Code03_RotateMatrix.java new file mode 100644 index 0000000..ee3f956 --- /dev/null +++ b/src/class39/Code03_RotateMatrix.java @@ -0,0 +1,44 @@ +package class39; + +public class Code03_RotateMatrix { + + public static void rotate(int[][] matrix) { + int a = 0; + int b = 0; + int c = matrix.length - 1; + int d = matrix[0].length - 1; + while (a < c) { + rotateEdge(matrix, a++, b++, c--, d--); + } + } + + public static void rotateEdge(int[][] m, int a, int b, int c, int d) { + int tmp = 0; + for (int i = 0; i < d - b; i++) { + tmp = m[a][b + i]; + m[a][b + i] = m[c - i][b]; + m[c - i][b] = m[c][d - i]; + m[c][d - i] = m[a + i][d]; + m[a + i][d] = tmp; + } + } + + public static void printMatrix(int[][] matrix) { + for (int i = 0; i != matrix.length; i++) { + for (int j = 0; j != matrix[0].length; j++) { + System.out.print(matrix[i][j] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } }; + printMatrix(matrix); + rotate(matrix); + System.out.println("========="); + printMatrix(matrix); + + } + +} diff --git a/src/class39/Code04_PrintMatrixSpiralOrder.java b/src/class39/Code04_PrintMatrixSpiralOrder.java new file mode 100644 index 0000000..27b8f30 --- /dev/null +++ b/src/class39/Code04_PrintMatrixSpiralOrder.java @@ -0,0 +1,53 @@ +package class39; + +public class Code04_PrintMatrixSpiralOrder { + + public static void spiralOrderPrint(int[][] matrix) { + int tR = 0; + int tC = 0; + int dR = matrix.length - 1; + int dC = matrix[0].length - 1; + while (tR <= dR && tC <= dC) { + printEdge(matrix, tR++, tC++, dR--, dC--); + } + } + + public static void printEdge(int[][] m, int tR, int tC, int dR, int dC) { + if (tR == dR) { + for (int i = tC; i <= dC; i++) { + System.out.print(m[tR][i] + " "); + } + } else if (tC == dC) { + for (int i = tR; i <= dR; i++) { + System.out.print(m[i][tC] + " "); + } + } else { + int curC = tC; + int curR = tR; + while (curC != dC) { + System.out.print(m[tR][curC] + " "); + curC++; + } + while (curR != dR) { + System.out.print(m[curR][dC] + " "); + curR++; + } + while (curC != tC) { + System.out.print(m[dR][curC] + " "); + curC--; + } + while (curR != tR) { + System.out.print(m[curR][tC] + " "); + curR--; + } + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, + { 13, 14, 15, 16 } }; + spiralOrderPrint(matrix); + + } + +} diff --git a/src/class39/Code05_ZigZagPrintMatrix.java b/src/class39/Code05_ZigZagPrintMatrix.java new file mode 100644 index 0000000..09089cb --- /dev/null +++ b/src/class39/Code05_ZigZagPrintMatrix.java @@ -0,0 +1,43 @@ +package class39; + +public class Code05_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 tR, int tC, int dR, int dC, + boolean f) { + if (f) { + while (tR != dR + 1) { + System.out.print(m[tR++][tC--] + " "); + } + } else { + while (dR != tR - 1) { + System.out.print(m[dR--][dC++] + " "); + } + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; + printMatrixZigZag(matrix); + + } + +} diff --git a/src/class39/Code06_PrintStar.java b/src/class39/Code06_PrintStar.java new file mode 100644 index 0000000..02f2b5d --- /dev/null +++ b/src/class39/Code06_PrintStar.java @@ -0,0 +1,46 @@ +package class39; + +public class Code06_PrintStar { + + public static void printStar(int N) { + int leftUp = 0; + int rightDown = N - 1; + char[][] m = new char[N][N]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + m[i][j] = ' '; + } + } + while (leftUp <= rightDown) { + set(m, leftUp, rightDown); + leftUp += 2; + rightDown -= 2; + } + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + System.out.print(m[i][j] + " "); + } + System.out.println(); + } + } + + public static void set(char[][] m, int leftUp, int rightDown) { + for (int col = leftUp; col <= rightDown; col++) { + m[leftUp][col] = '*'; + } + for (int row = leftUp + 1; row <= rightDown; row++) { + m[row][rightDown] = '*'; + } + for (int col = rightDown - 1; col > leftUp; col--) { + m[rightDown][col] = '*'; + } + for (int row = rightDown - 1; row > leftUp + 1; row--) { + m[row][leftUp + 1] = '*'; + } + } + + public static void main(String[] args) { + printStar(8); + } + +} diff --git a/src/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java b/src/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java new file mode 100644 index 0000000..a2389fa --- /dev/null +++ b/src/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java @@ -0,0 +1,91 @@ +package class40; + +public class Code01_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"); + } + +} diff --git a/src/class40/Code02_LongestSumSubArrayLength.java b/src/class40/Code02_LongestSumSubArrayLength.java new file mode 100644 index 0000000..c1a21aa --- /dev/null +++ b/src/class40/Code02_LongestSumSubArrayLength.java @@ -0,0 +1,90 @@ +package class40; + +import java.util.HashMap; + +public class Code02_LongestSumSubArrayLength { + + public static int maxLength(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + HashMap map = new HashMap(); + 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"); + + } + +} diff --git a/src/class40/Code03_LongestLessSumSubArrayLength.java b/src/class40/Code03_LongestLessSumSubArrayLength.java new file mode 100644 index 0000000..5349d2b --- /dev/null +++ b/src/class40/Code03_LongestLessSumSubArrayLength.java @@ -0,0 +1,105 @@ +package class40; + +public class Code03_LongestLessSumSubArrayLength { + + public static int maxLengthAwesome(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + int[] minSums = new int[arr.length]; + int[] minSumEnds = new int[arr.length]; + minSums[arr.length - 1] = arr[arr.length - 1]; + minSumEnds[arr.length - 1] = arr.length - 1; + for (int i = arr.length - 2; i >= 0; i--) { + if (minSums[i + 1] < 0) { + minSums[i] = arr[i] + minSums[i + 1]; + minSumEnds[i] = minSumEnds[i + 1]; + } else { + minSums[i] = arr[i]; + minSumEnds[i] = i; + } + } + int end = 0; + int sum = 0; + int res = 0; + // i是窗口的最左的位置,end扩出来的最右有效块儿的最后一个位置的,再下一个位置 + // end也是下一块儿的开始位置 + // 窗口:[i~end) + for (int i = 0; i < arr.length; i++) { + // while循环结束之后: + // 1) 如果以i开头的情况下,累加和<=k的最长子数组是arr[i..end-1],看看这个子数组长度能不能更新res; + // 2) 如果以i开头的情况下,累加和<=k的最长子数组比arr[i..end-1]短,更新还是不更新res都不会影响最终结果; + while (end < arr.length && sum + minSums[end] <= k) { + sum += minSums[end]; + end = minSumEnds[end] + 1; + } + res = Math.max(res, end - i); + if (end > i) { // 窗口内还有数 [i~end) [4,4) + sum -= arr[i]; + } else { // 窗口内已经没有数了,说明从i开头的所有子数组累加和都不可能<=k + end = i + 1; + } + } + return res; + } + + public static int maxLength(int[] arr, int k) { + int[] h = new int[arr.length + 1]; + int sum = 0; + h[0] = sum; + for (int i = 0; i != arr.length; i++) { + sum += arr[i]; + h[i + 1] = Math.max(sum, h[i]); + } + sum = 0; + int res = 0; + int pre = 0; + int len = 0; + for (int i = 0; i != arr.length; i++) { + sum += arr[i]; + pre = getLessIndex(h, sum - k); + len = pre == -1 ? 0 : i - pre + 1; + res = Math.max(res, len); + } + return res; + } + + public static int getLessIndex(int[] arr, int num) { + int low = 0; + int high = arr.length - 1; + int mid = 0; + int res = -1; + while (low <= high) { + mid = (low + high) / 2; + if (arr[mid] >= num) { + res = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return res; + } + + // for test + public static int[] generateRandomArray(int len, int maxValue) { + int[] res = new int[len]; + for (int i = 0; i != res.length; i++) { + res[i] = (int) (Math.random() * maxValue) - (maxValue / 3); + } + return res; + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int i = 0; i < 10000000; i++) { + int[] arr = generateRandomArray(10, 20); + int k = (int) (Math.random() * 20) - 5; + if (maxLengthAwesome(arr, k) != maxLength(arr, k)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/src/class40/Code04_AvgLessEqualValueLongestSubarray.java b/src/class40/Code04_AvgLessEqualValueLongestSubarray.java new file mode 100644 index 0000000..c56eef9 --- /dev/null +++ b/src/class40/Code04_AvgLessEqualValueLongestSubarray.java @@ -0,0 +1,152 @@ +package class40; + +import java.util.TreeMap; + +public class Code04_AvgLessEqualValueLongestSubarray { + + // 暴力解,时间复杂度O(N^3),用于做对数器 + public static int ways1(int[] arr, int v) { + int ans = 0; + for (int L = 0; L < arr.length; L++) { + for (int R = L; R < arr.length; R++) { + int sum = 0; + int k = R - L + 1; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + double avg = (double) sum / (double) k; + if (avg <= v) { + ans = Math.max(ans, k); + } + } + } + return ans; + } + + // 想实现的解法2,时间复杂度O(N*logN) + public static int ways2(int[] arr, int v) { + if (arr == null || arr.length == 0) { + return 0; + } + TreeMap origins = new TreeMap<>(); + int ans = 0; + int modify = 0; + for (int i = 0; i < arr.length; i++) { + int p1 = arr[i] <= v ? 1 : 0; + int p2 = 0; + int querry = -arr[i] - modify; + if (origins.floorKey(querry) != null) { + p2 = i - origins.get(origins.floorKey(querry)) + 1; + } + ans = Math.max(ans, Math.max(p1, p2)); + int curOrigin = -modify - v; + if (origins.floorKey(curOrigin) == null) { + origins.put(curOrigin, i); + } + modify += arr[i] - v; + } + return ans; + } + + // 想实现的解法3,时间复杂度O(N) + public static int ways3(int[] arr, int v) { + if (arr == null || arr.length == 0) { + return 0; + } + for (int i = 0; i < arr.length; i++) { + arr[i] -= v; + } + return maxLengthAwesome(arr, 0); + } + + // 找到数组中累加和<=k的最长子数组 + public static int maxLengthAwesome(int[] arr, int k) { + int N = arr.length; + int[] sums = new int[N]; + int[] ends = new int[N]; + sums[N - 1] = arr[N - 1]; + ends[N - 1] = N - 1; + for (int i = N - 2; i >= 0; i--) { + if (sums[i + 1] < 0) { + sums[i] = arr[i] + sums[i + 1]; + ends[i] = ends[i + 1]; + } else { + sums[i] = arr[i]; + ends[i] = i; + } + } + int end = 0; + int sum = 0; + int res = 0; + for (int i = 0; i < N; i++) { + while (end < N && sum + sums[end] <= k) { + sum += sums[end]; + end = ends[end] + 1; + } + res = Math.max(res, end - i); + if (end > i) { + sum -= arr[i]; + } else { + end = i + 1; + } + } + return res; + } + + // 用于测试 + public static int[] randomArray(int maxLen, int maxValue) { + int len = (int) (Math.random() * maxLen) + 1; + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * maxValue); + } + return ans; + } + + // 用于测试 + 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 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) { + System.out.println("测试开始"); + int maxLen = 20; + int maxValue = 100; + int testTime = 500000; + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int value = (int) (Math.random() * maxValue); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + int ans1 = ways1(arr1, value); + int ans2 = ways2(arr2, value); + int ans3 = ways3(arr3, value); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("测试出错!"); + System.out.print("测试数组:"); + printArray(arr); + System.out.println("子数组平均值不小于 :" + value); + System.out.println("方法1得到的最大长度:" + ans1); + System.out.println("方法2得到的最大长度:" + ans2); + System.out.println("方法3得到的最大长度:" + ans3); + System.out.println("========================="); + } + } + System.out.println("测试结束"); + } + +} diff --git a/src/class41/Code01_NiuNiuSplitField.java b/src/class41/Code01_NiuNiuSplitField.java new file mode 100644 index 0000000..d98b48e --- /dev/null +++ b/src/class41/Code01_NiuNiuSplitField.java @@ -0,0 +1,179 @@ +package class41; + +import java.util.*; + +public class Code01_NiuNiuSplitField { + + // arr都是正数,0~i切的时候,怎么切最优,最优的结论生成一个数组返回 + public static int[] getAnswers(int[] arr) { + if (arr == null || arr.length < 1) { + return null; + } + // ans[i] 代表的含义: + // arr[0..i]切两块的各种方案中,哪一种方案能让左右两部份的累加和最小值,尽量大 + int[] ans = new int[arr.length]; + ans[0] = 0; + int best = 0;// 左 0..0 右 无 + for (int i = 1; i < arr.length; i++) { + while (best < i - 1 && getSplit(arr, best, i) < getSplit(arr, best + 1, i)) { + best++; + } + ans[i] = getSplit(arr, best, i); + } + return ans; + } + + public static int getSum(int[] arr, int i, int j) { + return 0; + } + + // arr[0..j] 如果我把切的这一刀定在m位置,左0..m,右m+1..j + public static int getSplit(int[] arr, int m, int j) { + return 0; + } + + public static int maxMinSumIn16(int[][] matrix) { + if (matrix == null || matrix.length < 4 || matrix[0].length < 4) { + return 0; + } + // help[i][j] 含义,左上角为(0,0) 右下角为(i,j)的子矩阵累加和是多少 + int[][] help = generateSumRecord(matrix); + // 通过help,得到任何子矩阵的累加和 + int col = matrix[0].length; + int res = Integer.MIN_VALUE; + // 3个for循环在暴力枚举纵向三刀 + for (int c1 = 0; c1 < col - 3; c1++) { + for (int c2 = c1 + 1; c2 < col - 2; c2++) { + for (int c3 = c2 + 1; c3 < col - 1; c3++) { + // c1 c2 c3 + res = Math.max(res, getBestDicision(help, c1, c2, c3)); + } + } + } + return res; + } + + // record[i][j] 左上角点0,0, 有效角点i、j , 该矩阵的累加和 + public static int[][] generateSumRecord(int[][] matrix) { + int row = matrix.length; + int col = matrix[0].length; + int[][] record = new int[row][col]; + record[0][0] = matrix[0][0]; + for (int i = 1; i < row; i++) { + record[i][0] = record[i - 1][0] + matrix[i][0]; + } + for (int j = 1; j < col; j++) { + record[0][j] = record[0][j - 1] + matrix[0][j]; + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + record[i][j] = record[i][j - 1] + record[i - 1][j] - record[i - 1][j - 1] + matrix[i][j]; + } + } + return record; + } + + // 忘掉原来的矩阵,只使用help + // 竖着三刀固定,c1,c2,c3 + // 所有横着切三刀的可能性中,那种切法能得到最好的结果,把结果返回 + public static int getBestDicision(int[][] help, int c1, int c2, int c3) { + // 0~i 切一刀,出来八块,最优指标,up[i] + int[] up = getUpSplitArray(help, c1, c2, c3); // 0行~i行 切一刀,分8块,min max + + // i ~ N-1 切一刀,出来八块,最优指标,down[i] + int[] down = getDownSplitArray(help, c1, c2, c3); // i~n-1 切一刀 + + int res = Integer.MIN_VALUE; + for (int mid = 1; mid < help.length - 2; mid++) { + res = Math.max(res, Math.min(up[mid], down[mid + 1])); + } + return res; + } + + public static int value(int[][] record, int c1, int c2, int c3, int prow, int crow) { + int value1 = area(record, prow, 0, crow, c1); + int value2 = area(record, prow, c1 + 1, crow, c2); + int value3 = area(record, prow, c2 + 1, crow, c3); + int value4 = area(record, prow, c3 + 1, crow, record[0].length - 1); + return Math.min(Math.min(value1, value2), Math.min(value3, value4)); + } + + public static int area(int[][] record, int i1, int j1, int i2, int j2) { + int all = record[i2][j2]; + int left = j1 > 0 ? record[i2][j1 - 1] : 0; + int up = i1 > 0 ? record[i1 - 1][j2] : 0; + int makeUp = (i1 > 0 && j1 > 0) ? record[i1 - 1][j1 - 1] : 0; + return all - left - up + makeUp; + } + + public static int[] getUpSplitArray(int[][] record, int c1, int c2, int c3) { + int size = record.length; + int[] up = new int[size]; + int split = 0; + up[1] = Math.min(value(record, c1, c2, c3, 0, 0), value(record, c1, c2, c3, 1, 1)); + for (int i = 2; i < size; i++) { + int minsMax = towSubMatrixMin(record, c1, c2, c3, 0, split, i); + while (split < i) { + if (split == i - 1) { + break; + } + int moved = towSubMatrixMin(record, c1, c2, c3, 0, split + 1, i); + if (moved < minsMax) { + break; + } else { + minsMax = moved; + split++; + } + } + up[i] = minsMax; + } + return up; + } + + public static int[] getDownSplitArray(int[][] record, int c1, int c2, int c3) { + int size = record.length; + int[] down = new int[size]; + int split = size - 1; + down[size - 2] = Math.min(value(record, c1, c2, c3, size - 2, size - 2), + value(record, c1, c2, c3, size - 1, size - 1)); + for (int i = size - 3; i >= 0; i--) { + int minsMax = towSubMatrixMin(record, c1, c2, c3, i, split - 1, size - 1); + while (split > i) { + if (split == i + 1) { + break; + } + int moved = towSubMatrixMin(record, c1, c2, c3, i, split - 2, size - 1); + if (moved < minsMax) { + break; + } else { + minsMax = moved; + split--; + } + } + down[i] = minsMax; + } + return down; + } + + public static int towSubMatrixMin(int[][] record, int c1, int c2, int c3, int i, int split, int j) { + return Math.min(value(record, c1, c2, c3, i, split), value(record, c1, c2, c3, split + 1, j)); + } + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + while (in.hasNext()) { + int n = in.nextInt(); + int m = in.nextInt(); + int[][] matrix = new int[n][m]; + for (int i = 0; i < n; i++) { + char[] chas = in.next().toCharArray(); + for (int j = 0; j < m; j++) { + matrix[i][j] = chas[j] - '0'; + } + } + System.out.println(maxMinSumIn16(matrix)); + } + in.close(); + } + +} \ No newline at end of file diff --git a/src/class41/Code02_SplitArrayLargestSum.java b/src/class41/Code02_SplitArrayLargestSum.java new file mode 100644 index 0000000..0de1cea --- /dev/null +++ b/src/class41/Code02_SplitArrayLargestSum.java @@ -0,0 +1,124 @@ +package class41; + +public class Code02_SplitArrayLargestSum { + + public static int splitArray1(int[] nums, int m) { + return process(nums, 0, m); + } + + public static int process(int[] arr, int index, int part) { + if (index == arr.length) { + return 0; + } + if (part == 0) { + return -1; + } + int first = 0; + int min = Integer.MAX_VALUE; + for (int end = index; arr.length - end >= part; end++) { + first += arr[end]; + int next = process(arr, end + 1, part - 1); + if (next != -1) { + min = Math.min(min, Math.max(first, next)); + } + } + return min; + } + + public static int splitArray2(int[] nums, int M) { + int N = nums.length; + int[] help = new int[nums.length + 1]; + for (int i = 0; i < N; i++) { + help[i + 1] = help[i] + nums[i]; + } + int[][] dp = new int[N][M + 1]; + for (int i = 0; i < N; i++) { + dp[i][1] = help[i + 1] - help[0]; + } + for (int i = 1; i < Math.min(N, M); i++) { + dp[i][i + 1] = Math.max(dp[i - 1][i], nums[i]); + } + for (int i = 2; i < N; i++) { + for (int j = 2; j <= Math.min(i, M); j++) { + dp[i][j] = Integer.MAX_VALUE; + for (int k = i; k >= j - 1; k--) { + dp[i][j] = Math.min(dp[i][j], Math.max(dp[k - 1][j - 1], help[i + 1] - help[k])); + } + } + } + return dp[N - 1][M]; + } + + public static int splitArray3(int[] nums, int M) { + int N = nums.length; + int[] help = new int[nums.length + 1]; + for (int i = 0; i < N; i++) { + help[i + 1] = help[i] + nums[i]; + } + int[][] dp = new int[N][M + 1]; + int[][] best = new int[N][M + 1]; + for (int i = 0; i < N; i++) { + dp[i][1] = help[i + 1] - help[0]; + } + for (int i = 1; i < Math.min(N, M); i++) { + dp[i][i + 1] = Math.max(dp[i - 1][i], nums[i]); + best[i][i + 1] = i; + } + for (int i = 2; i < N; i++) { + for (int j = Math.min(i, M); j >= 2; j--) { + dp[i][j] = Integer.MAX_VALUE; + int left = best[i - 1][j]; + int right = j + 1 > M ? i : best[i][j + 1]; + for (int k = left; k <= right; k++) { + int curAns = Math.max(dp[k - 1][j - 1], help[i + 1] - help[k]); + if (dp[i][j] > curAns) { + dp[i][j] = curAns; + best[i][j] = k; + } + } + } + } + return dp[N - 1][M]; + } + + public static int splitArray4(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; + } + +} diff --git a/src/class42/Code01_ThrowChessPiecesProblem.java b/src/class42/Code01_ThrowChessPiecesProblem.java new file mode 100644 index 0000000..2aa44ff --- /dev/null +++ b/src/class42/Code01_ThrowChessPiecesProblem.java @@ -0,0 +1,194 @@ +package class42; + +public class Code01_ThrowChessPiecesProblem { + + public static int solution1(int nLevel, int kChess) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + return Process1(nLevel, kChess); + } + + // rest还剩多少层楼需要去验证 + // k还有多少颗棋子能够使用 + // 一定要验证出最高的不会碎的楼层!但是每次都是坏运气。 + // 返回至少需要扔几次? + public static int Process1(int rest, int k) { + if (rest == 0) { + return 0; + } + if (k == 1) { + return rest; + } + int min = Integer.MAX_VALUE; + for (int i = 1; i != rest + 1; i++) { // 第一次扔的时候,仍在了i层 + min = Math.min(min, + Math.max(Process1(i - 1, k - 1), Process1(rest - i, k))); + } + return min + 1; + } + + public static int solution2(int nLevel, int kChess) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + if (kChess == 1) { + return nLevel; + } + int[][] dp = new int[nLevel + 1][kChess + 1]; + for (int i = 1; i != dp.length; i++) { + dp[i][1] = i; + } + for (int i = 1; i != dp.length; i++) { + for (int j = 2; j != dp[0].length; j++) { + int min = Integer.MAX_VALUE; + for (int k = 1; k != i + 1; k++) { + min = Math.min(min, Math.max(dp[k - 1][j - 1], dp[i - k][j])); + } + dp[i][j] = min + 1; + } + } + return dp[nLevel][kChess]; + } + + public static int solution3(int nLevel, int kChess) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + if (kChess == 1) { + return nLevel; + } + int[] preArr = new int[nLevel + 1]; + int[] curArr = new int[nLevel + 1]; + for (int i = 1; i != curArr.length; i++) { + curArr[i] = i; + } + for (int i = 1; i != kChess; i++) { + int[] tmp = preArr; + preArr = curArr; + curArr = tmp; + for (int j = 1; j != curArr.length; j++) { + int min = Integer.MAX_VALUE; + for (int k = 1; k != j + 1; k++) { + min = Math.min(min, Math.max(preArr[k - 1], curArr[j - k])); + } + curArr[j] = min + 1; + } + } + return curArr[curArr.length - 1]; + } + + public static int solution4(int nLevel, int kChess) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + if (kChess == 1) { + return nLevel; + } + int[][] dp = new int[nLevel + 1][kChess + 1]; + for (int i = 1; i != dp.length; i++) { + dp[i][1] = i; + } + int[] cands = new int[kChess + 1]; + for (int i = 1; i != dp[0].length; i++) { + dp[1][i] = 1; + cands[i] = 1; + } + for (int i = 2; i < nLevel + 1; i++) { + for (int j = kChess; j > 1; j--) { + int min = Integer.MAX_VALUE; + int minEnum = cands[j]; + int maxEnum = j == kChess ? i / 2 + 1 : cands[j + 1]; + for (int k = minEnum; k < maxEnum + 1; k++) { + int cur = Math.max(dp[k - 1][j - 1], dp[i - k][j]); + if (cur <= min) { + min = cur; + cands[j] = k; + } + } + dp[i][j] = min + 1; + } + } + return dp[nLevel][kChess]; + } + + public static int solution5(int nLevel, int kChess) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + int bsTimes = log2N(nLevel) + 1; + if (kChess >= bsTimes) { + return bsTimes; + } + int[] dp = new int[kChess]; + int res = 0; + while (true) { + res++; + int previous = 0; + for (int i = 0; i < dp.length; i++) { + int tmp = dp[i]; + dp[i] = dp[i] + previous + 1; + previous = tmp; + if (dp[i] >= nLevel) { + return res; + } + } + } + } + + public static int log2N(int n) { + int res = -1; + while (n != 0) { + res++; + n >>>= 1; + } + return res; + } + + public static void main(String[] args) { + System.out.println(solution1(21, 2)); + System.out.println(solution2(21, 2)); + System.out.println(solution3(21, 2)); + System.out.println(solution4(21, 2)); + System.out.println(solution5(21, 2)); + + System.out.println("=============="); + + System.out.println(solution2(105, 2)); + System.out.println(solution3(105, 2)); + System.out.println(solution4(105, 2)); + System.out.println(solution5(105, 2)); + + System.out.println("=============="); + + System.out.println(solution2(3000, 10)); + System.out.println(solution3(3000, 10)); + System.out.println(solution4(3000, 10)); + System.out.println(solution5(3000, 10)); + + System.out.println("=============="); + + System.out.println(solution2(6884, 5)); + System.out.println(solution3(6884, 5)); + System.out.println(solution4(6884, 5)); + System.out.println(solution5(6884, 5)); + + System.out.println("=============="); + + System.out.println(solution2(6885, 5)); + System.out.println(solution3(6885, 5)); + System.out.println(solution4(6885, 5)); + System.out.println(solution5(6885, 5)); + + System.out.println("=============="); + + int nLevel = 100000000; + int kChess = 10; + long start = System.currentTimeMillis(); + System.out.println(solution5(nLevel, kChess)); + long end = System.currentTimeMillis(); + System.out.println("cost time: " + (end - start) + " ms"); + + } + +} diff --git a/src/class42/Code02_PostOfficeProblem.java b/src/class42/Code02_PostOfficeProblem.java new file mode 100644 index 0000000..8f91c03 --- /dev/null +++ b/src/class42/Code02_PostOfficeProblem.java @@ -0,0 +1,215 @@ +package class42; + +import java.util.Arrays; + +public class Code02_PostOfficeProblem { + + public static int minDis1(int[] arr, int num) { + if (arr == null || arr.length < 2 || num < 1) { + return 0; + } + // record[L][R]表示如果arr[L...R]上只建立一个邮局,总距离最小是多少? + int[][] record = getRecord(arr); + int N = arr.length; + int[][] dp = new int[N][num + 1]; + // dp[...][0] 0个邮局的时候如何如何 + // dp[0][...] 0 + for (int i = 0; i < N; i++) { + // 0...i 1 + dp[i][1] = record[0][i]; + } + for (int i = 1; i < N; i++) { + for (int j = 2; j <= Math.min(num, i); j++) { + // dp[i][j] + // 枚举最后一个邮局负责的范围,K...i + // i..i + // i-1..i + // i-2..i + // 1....i + // 0....i 单列 + // 0...k-1 j-1个邮局 + + dp[i][j] = record[0][i]; + for (int k = i; k > 0; k--) { // 1 .... i + dp[i][j] = Math.min(dp[i][j], dp[k - 1][j - 1] + record[k][i]); + } + } + } + return dp[N - 1][num]; + } + + public static int minDis2(int[] arr, int num) { + if (arr == null || arr.length < 2 || num < 1) { + return 0; + } + // record[L][R]表示如果arr[L...R]上只建立一个邮局,总距离最小是多少? + int[][] record = getRecord(arr); + int N = arr.length; + int[][] dp = new int[N][num + 1]; + // dp[...][0] + // dp[0][...] 0..0 0 + // choose[0][..] 0 + // choose[i][j] 当时在求dp[i][j]的时候, + // 最右的邮局,如果是在负责k...i这一段的时候,取得的最优解,请把choose[i][j] = k + int[][] choose = new int[N][num + 1]; + for (int i = 0; i < N; i++) { + // 0..i 1个邮局 0...i 0 + dp[i][1] = record[0][i]; + } + for (int i = 1; i < N; i++) { + for (int j = Math.min(num, i); j >= 2; j--) { + int down = choose[i - 1][j]; + int up = j == Math.min(num, i) ? i : choose[i][j + 1]; + dp[i][j] = record[0][i]; + for (int k = Math.max(1, down); k <= Math.min(up, i); k++) { + if (dp[k - 1][j - 1] + record[k][i] < dp[i][j]) { + dp[i][j] = dp[k - 1][j - 1] + record[k][i]; + choose[i][j] = k; + } + } + } + } + return dp[N - 1][num]; + } + + public static int[][] getRecord(int[] arr) { + int N = arr.length; + int[][] record = new int[N][N]; + for (int L = 0; L < N; L++) { + for (int R = L + 1; R < N; R++) { + record[L][R] = record[L][R - 1] + arr[R] - arr[(L + R) >> 1]; + } + } + return record; + } + + public static int minDistances1(int[] arr, int num) { + if (arr == null || num < 1 || arr.length < num) { + return 0; + } + int[][] w = new int[arr.length + 1][arr.length + 1]; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + w[i][j] = w[i][j - 1] + arr[j] - arr[(i + j) / 2]; + } + } + int[][] dp = new int[num][arr.length]; + for (int j = 0; j != arr.length; j++) { + dp[0][j] = w[0][j]; + } + for (int i = 1; i < num; i++) { + for (int j = i + 1; j < arr.length; j++) { + dp[i][j] = Integer.MAX_VALUE; + for (int k = 0; k <= j; k++) { + dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + w[k + 1][j]); + } + } + } + return dp[num - 1][arr.length - 1]; + } + + public static int minDistances2(int[] arr, int num) { + if (arr == null || num < 1 || arr.length < num) { + return 0; + } + int[][] w = new int[arr.length + 1][arr.length + 1]; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + w[i][j] = w[i][j - 1] + arr[j] - arr[(i + j) / 2]; + } + } + int[][] dp = new int[num][arr.length]; + int[][] s = new int[num][arr.length]; + for (int j = 0; j != arr.length; j++) { + dp[0][j] = w[0][j]; + s[0][j] = 0; + } + int minK = 0; + int maxK = 0; + int cur = 0; + for (int i = 1; i < num; i++) { + for (int j = arr.length - 1; j > i; j--) { + minK = s[i - 1][j]; + maxK = j == arr.length - 1 ? arr.length - 1 : s[i][j + 1]; + dp[i][j] = Integer.MAX_VALUE; + for (int k = minK; k <= maxK; k++) { + cur = dp[i - 1][k] + w[k + 1][j]; + if (cur <= dp[i][j]) { + dp[i][j] = cur; + s[i][j] = k; + } + } + } + } + return dp[num - 1][arr.length - 1]; + } + + // for test + public static int[] getSortedArray(int len, int range) { + int[] arr = new int[len]; + for (int i = 0; i != len; i++) { + arr[i] = (int) (Math.random() * range); + } + Arrays.sort(arr); + return arr; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int[] arr = { 1,3,8,10,12 }; + int num = 3; + System.out.println(minDis1(arr, num)); + System.out.println(minDistances1(arr, num)); + System.out.println(minDistances2(arr, num)); + + int times = 100; // test time + int len = 1000; // test array length + int range = 2000; // every number in [0,range) + int p = 50; // post office number max + long time1 = 0; // method1 all run time + long time2 = 0;// method2 all run time + long time3 = 0; + long start = 0; + long end = 0; + int res1 = 0; + int res2 = 0; + int res3 = 0; + for (int i = 0; i != times; i++) { + int office = (int) (Math.random() * p) + 1; + arr = getSortedArray(len, range); + start = System.currentTimeMillis(); + res1 = minDistances1(arr, office); + end = System.currentTimeMillis(); + time1 += end - start; + start = System.currentTimeMillis(); + res2 = minDis2(arr, office); + end = System.currentTimeMillis(); + time2 += end - start; + + start = System.currentTimeMillis(); + res3 = minDis1(arr, office); + end = System.currentTimeMillis(); + time3 += end - start; + if (res1 != res2 || res1 != res3) { + printArray(arr); + break; + } + if (i % 10 == 0) { + System.out.print(". "); + } + } + System.out.println(); + System.out.println("method1 all run time(ms): " + time1); + System.out.println("method2 all run time(ms): " + time2); + System.out.println("method3 all run time(ms): " + time3); + + } + +} diff --git a/src/class43/Code01_TSP.java b/src/class43/Code01_TSP.java new file mode 100644 index 0000000..87bd69c --- /dev/null +++ b/src/class43/Code01_TSP.java @@ -0,0 +1,295 @@ +package class43; + +import java.util.ArrayList; +import java.util.List; + +public class Code01_TSP { + + public static int t1(int[][] matrix) { + int N = matrix.length; // 0...N-1 + // set + // set.get(i) != null i这座城市在集合里 + // set.get(i) == null i这座城市不在集合里 + List set = new ArrayList<>(); + for (int i = 0; i < N; i++) { + set.add(1); + } + return func1(matrix, set, 0); + } + + // 任何两座城市之间的距离,可以在matrix里面拿到 + // set中表示着哪些城市的集合, + // start这座城一定在set里, + // 从start出发,要把set中所有的城市过一遍,最终回到0这座城市,最小距离是多少 + public static int func1(int[][] matrix, List set, int start) { + int cityNum = 0; + for (int i = 0; i < set.size(); i++) { + if (set.get(i) != null) { + cityNum++; + } + } + if (cityNum == 1) { + return matrix[start][0]; + } + // 不只start这一座城 + set.set(start, null); + int min = Integer.MAX_VALUE; + for (int i = 0; i < set.size(); i++) { + if (set.get(i) != null && i != start) { + // start -> i i... -> 0 + int cur = matrix[start][i] + func1(matrix, set, i); + min = Math.min(min, cur); + } + } + set.set(start, 1); + return min; + } + + public static int t2(int[][] matrix) { + int N = matrix.length; // 0...N-1 + // 7座城 1111111 + int allCity = (1 << N) - 1; + return f2(matrix, allCity, 0); + } + + // 任何两座城市之间的距离,可以在matrix里面拿到 + // set中表示着哪些城市的集合, + // start这座城一定在set里, + // 从start出发,要把set中所有的城市过一遍,最终回到0这座城市,最小距离是多少 + public static int f2(int[][] matrix, int cityStatus, int start) { + // cityStatus == cityStatux & (~cityStaus + 1) + + if (cityStatus == (cityStatus & (~cityStatus + 1))) { + return matrix[start][0]; + } + + // 把start位的1去掉, + cityStatus &= (~(1 << start)); + int min = Integer.MAX_VALUE; + // 枚举所有的城市 + for (int move = 0; move < matrix.length; move++) { + if (move != start && (cityStatus & (1 << move)) != 0) { + int cur = matrix[start][move] + f2(matrix, cityStatus, move); + min = Math.min(min, cur); + } + } + cityStatus |= (1 << start); + return min; + } + + public static int t3(int[][] matrix) { + int N = matrix.length; // 0...N-1 + // 7座城 1111111 + int allCity = (1 << N) - 1; + int[][] dp = new int[1 << N][N]; + for (int i = 0; i < (1 << N); i++) { + for (int j = 0; j < N; j++) { + dp[i][j] = -1; + } + } + return f3(matrix, allCity, 0, dp); + } + + // 任何两座城市之间的距离,可以在matrix里面拿到 + // set中表示着哪些城市的集合, + // start这座城一定在set里, + // 从start出发,要把set中所有的城市过一遍,最终回到0这座城市,最小距离是多少 + public static int f3(int[][] matrix, int cityStatus, int start, int[][] dp) { + if (dp[cityStatus][start] != -1) { + return dp[cityStatus][start]; + } + if (cityStatus == (cityStatus & (~cityStatus + 1))) { + dp[cityStatus][start] = matrix[start][0]; + } else { + // 把start位的1去掉, + cityStatus &= (~(1 << start)); + int min = Integer.MAX_VALUE; + // 枚举所有的城市 + for (int move = 0; move < matrix.length; move++) { + if (move != start && (cityStatus & (1 << move)) != 0) { + int cur = matrix[start][move] + f3(matrix, cityStatus, move, dp); + min = Math.min(min, cur); + } + } + cityStatus |= (1 << start); + dp[cityStatus][start] = min; + } + return dp[cityStatus][start]; + } + + public static int t4(int[][] matrix) { + int N = matrix.length; // 0...N-1 + int statusNums = 1 << N; + int[][] dp = new int[statusNums][N]; + + for (int status = 0; status < statusNums; status++) { + for (int start = 0; start < N; start++) { + if ((status & (1 << start)) != 0) { + if (status == (status & (~status + 1))) { + dp[status][start] = matrix[start][0]; + } else { + int min = Integer.MAX_VALUE; + // start 城市在status里去掉之后,的状态 + int preStatus = status & (~(1 << start)); + // start -> i + for (int i = 0; i < N; i++) { + if ((preStatus & (1 << i)) != 0) { + int cur = matrix[start][i] + dp[preStatus][i]; + min = Math.min(min, cur); + } + } + dp[status][start] = min; + } + } + } + } + return dp[statusNums - 1][0]; + } + + // matrix[i][j] -> i城市到j城市的距离 + public static int tsp1(int[][] matrix, int origin) { + if (matrix == null || matrix.length < 2 || origin < 0 || origin >= matrix.length) { + return 0; + } + // 要考虑的集合 + ArrayList cities = new ArrayList<>(); + // cities[0] != null 表示0城在集合里 + // cities[i] != null 表示i城在集合里 + for (int i = 0; i < matrix.length; i++) { + cities.add(1); + } + // null,1,1,1,1,1,1 + // origin城不参与集合 + cities.set(origin, null); + return process(matrix, origin, cities, origin); + } + + // matrix 所有距离,存在其中 + // origin 固定参数,唯一的目标 + // cities 要考虑的集合,一定不含有origin + // 当前来到的城市是谁,cur + public static int process(int[][] matrix, int aim, ArrayList cities, int cur) { + boolean hasCity = false; // 集团中还是否有城市 + int ans = Integer.MAX_VALUE; + for (int i = 0; i < cities.size(); i++) { + if (cities.get(i) != null) { + hasCity = true; + cities.set(i, null); + // matrix[cur][i] + f(i, 集团(去掉i) ) + ans = Math.min(ans, matrix[cur][i] + process(matrix, aim, cities, i)); + cities.set(i, 1); + } + } + return hasCity ? ans : matrix[cur][aim]; + } + + // cities 里,一定含有cur这座城 + // 解决的是,集合从cur出发,通过集合里所有的城市,最终来到aim,最短距离 + public static int process2(int[][] matrix, int aim, ArrayList cities, int cur) { + if (cities.size() == 1) { + return matrix[cur][aim]; + } + cities.set(cur, null); + int ans = Integer.MAX_VALUE; + for (int i = 0; i < cities.size(); i++) { + if (cities.get(i) != null) { + int dis = matrix[cur][i] + process2(matrix, aim, cities, i); + ans = Math.min(ans, dis); + } + } + cities.set(cur, 1); + return ans; + } + + public static int tsp2(int[][] matrix, int origin) { + if (matrix == null || matrix.length < 2 || origin < 0 || origin >= matrix.length) { + return 0; + } + int N = matrix.length - 1; // 除去origin之后是n-1个点 + int S = 1 << N; // 状态数量 + int[][] dp = new int[S][N]; + int icity = 0; + int kcity = 0; + for (int i = 0; i < N; i++) { + icity = i < origin ? i : i + 1; + // 00000000 i + dp[0][i] = matrix[icity][origin]; + } + for (int status = 1; status < S; status++) { + // 尝试每一种状态 status = 0 0 1 0 0 0 0 0 0 + // 下标 8 7 6 5 4 3 2 1 0 + for (int i = 0; i < N; i++) { + // i 枚举的出发城市 + dp[status][i] = Integer.MAX_VALUE; + if ((1 << i & status) != 0) { + // 如果i这座城是可以枚举的,i = 6 , i对应的原始城的编号,icity + icity = i < origin ? i : i + 1; + for (int k = 0; k < N; k++) { // i 这一步连到的点,k + if ((1 << k & status) != 0) { // i 这一步可以连到k + kcity = k < origin ? k : k + 1; // k对应的原始城的编号,kcity + dp[status][i] = Math.min(dp[status][i], dp[status ^ (1 << i)][k] + matrix[icity][kcity]); + } + } + } + } + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + icity = i < origin ? i : i + 1; + ans = Math.min(ans, dp[S - 1][i] + matrix[origin][icity]); + } + return ans; + } + + public static int[][] generateGraph(int maxSize, int maxValue) { + int len = (int) (Math.random() * maxSize) + 1; + int[][] matrix = new int[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + matrix[i][j] = (int) (Math.random() * maxValue) + 1; + } + } + for (int i = 0; i < len; i++) { + matrix[i][i] = 0; + } + return matrix; + } + + public static void main(String[] args) { + int len = 10; + int value = 100; + System.out.println("功能测试开始"); + for (int i = 0; i < 20000; i++) { + int[][] matrix = generateGraph(len, value); + int origin = (int) (Math.random() * matrix.length); + int ans1 = t3(matrix); + int ans2 = t4(matrix); + int ans3 = tsp2(matrix, origin); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("fuck"); + } + } + System.out.println("功能测试结束"); + + len = 22; + System.out.println("性能测试开始,数据规模 : " + len); + int[][] matrix = new int[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + matrix[i][j] = (int) (Math.random() * value) + 1; + } + } + for (int i = 0; i < len; i++) { + matrix[i][i] = 0; + } + long start; + long end; + start = System.currentTimeMillis(); + t4(matrix); + end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + + } + +} diff --git a/src/class43/Code02_PavingTile.java b/src/class43/Code02_PavingTile.java new file mode 100644 index 0000000..18b3b6d --- /dev/null +++ b/src/class43/Code02_PavingTile.java @@ -0,0 +1,131 @@ +package class43; + +public class Code02_PavingTile { + + /* + * 2*M铺地的问题非常简单,这个是解决N*M铺地的问题 + */ + + public static int ways1(int N, int M) { + if (N < 1 || M < 1 || ((N * M) & 1) != 0) { + return 0; + } + if (N == 1 || M == 1) { + return 1; + } + int[] pre = new int[M]; // pre代表-1行的状况 + for (int i = 0; i < pre.length; i++) { + pre[i] = 1; + } + return process(pre, 0, N); + } + + // pre 表示level-1行的状态 + // level表示,正在level行做决定 + // N 表示一共有多少行 固定的 + // level-2行及其之上所有行,都摆满砖了 + // level做决定,让所有区域都满,方法数返回s + public static int process(int[] pre, int level, int N) { + if (level == N) { // base case + for (int i = 0; i < pre.length; i++) { + if (pre[i] == 0) { + return 0; + } + } + return 1; + } + + // 没到终止行,可以选择在当前的level行摆瓷砖 + int[] op = getOp(pre); + return dfs(op, 0, level, N); + } + + // op[i] == 0 可以考虑摆砖 + // op[i] == 1 只能竖着向上 + public static int dfs(int[] op, int col, int level, int N) { + // 在列上自由发挥,玩深度优先遍历,当col来到终止列,i行的决定做完了 + // 轮到i+1行,做决定 + if (col == op.length) { + return process(op, level + 1, N); + } + int ans = 0; + // col位置不横摆 + ans += dfs(op, col + 1, level, N); // col位置上不摆横转 + // col位置横摆, 向右 + if (col + 1 < op.length && op[col] == 0 && op[col + 1] == 0) { + op[col] = 1; + op[col + 1] = 1; + ans += dfs(op, col + 2, level, N); + op[col] = 0; + op[col + 1] = 0; + } + return ans; + } + + public static int[] getOp(int[] pre) { + int[] cur = new int[pre.length]; + for (int i = 0; i < pre.length; i++) { + cur[i] = pre[i] ^ 1; + } + return cur; + } + + public static int ways2(int N, int M) { + if (N < 1 || M < 1 || ((N * M) & 1) != 0) { + return 0; + } + if (N == 1 || M == 1) { + return 1; + } + int big = N > M ? N : M; + int small = big == N ? M : N; + // big * small + int sn = 1 << small; + int limit = sn - 1; // 全满状态 + int[] dp = new int[sn]; + dp[limit] = 1; // -1行全是满的 + // dp -1 行的状况 + // dp[0000] 0 + // dp[0001] 0 + // dp[1111] 1 + int[] cur = new int[sn]; // 当前行, 要算出所有状态下的解 + for (int level = 0; level < big; level++) { + for (int status = 0; status < sn; status++) { + if (dp[status] != 0) { // 状态出现了 + // 0...1100 + // 1...0011 + // 0...1111 + // 0...0011 + int op = (~status) & limit; + dfs(dp[status], op, 0, small - 1, cur); + } + } + for (int i = 0; i < sn; i++) { + dp[i] = 0; + } + int[] tmp = dp; + dp = cur; + cur = tmp; + } + return dp[limit]; + } + + public static void dfs(int way, int op, int index, int end, int[] cur) { + if (index == end) { + cur[op] += way; + } else { + dfs(way, op, index + 1, end, cur); + if (((3 << index) & op) == 0) { // 11 << index 可以放砖 + dfs(way, op | (3 << index), index + 1, end, cur); + } + } + } + + public static void main(String[] args) { + int N = 8; + int M = 8; + System.out.println(ways1(N, M)); + System.out.println(ways2(N, M)); + } + +} diff --git a/src/class43/Code03_CanIWin.java b/src/class43/Code03_CanIWin.java new file mode 100644 index 0000000..c0221c7 --- /dev/null +++ b/src/class43/Code03_CanIWin.java @@ -0,0 +1,64 @@ +package class43; + +// leetcode 464题 +public class Code03_CanIWin { + + // 这个是暴力尝试,思路是正确的,超时而已 + public static boolean canIWin1(int choose, int total) { + if (total == 0) { + return true; + } + if ((choose * (choose + 1) >> 1) < total) { + return false; + } + return process1(choose, 0, total); + } + + public static boolean process1(int choose, int status, int rest) { + if (rest <= 0) { + return false; + } + for (int i = 1; i <= choose; i++) { + if (((1 << i) & status) == 0) { + if (!process1(choose, (status | (1 << i)), rest - i)) { + return true; + } + } + } + return false; + } + + // 暴力尝试改动态规划而已 + public static boolean canIWin2(int choose, int total) { + if (total == 0) { + return true; + } + if ((choose * (choose + 1) >> 1) < total) { + return false; + } + int[] dp = new int[1 << (choose + 1)]; + return process2(choose, 0, total, dp); + } + + // 为什么明明status和rest是两个可变参数,却只用status来代表状态(也就是dp) + // 因为选了一批数字之后,得到的和一定是一样的,所以rest是由status决定的,所以rest不需要参与记忆化搜索 + public static boolean process2(int choose, int status, int rest, int[] dp) { + if (dp[status] != 0) { + return dp[status] == 1 ? true : false; + } + boolean ans = false; + if (rest > 0) { + for (int i = 1; i <= choose; i++) { + if (((1 << i) & status) == 0) { + if (!process2(choose, (status | (1 << i)), rest - i, dp)) { + ans = true; + break; + } + } + } + } + dp[status] = ans ? 1 : -1; + return ans; + } + +} diff --git a/src/class44/Code01_CreateMaximumNumber.java b/src/class44/Code01_CreateMaximumNumber.java new file mode 100644 index 0000000..bb5e182 --- /dev/null +++ b/src/class44/Code01_CreateMaximumNumber.java @@ -0,0 +1,248 @@ +package class44; + +// 测试链接: https://leetcode.com/problems/create-maximum-number/ +public class Code01_CreateMaximumNumber { + + public static int[] maxNumber1(int[] nums1, int[] nums2, int k) { + int len1 = nums1.length; + int len2 = nums2.length; + if (k < 0 || k > len1 + len2) { + return null; + } + int[] res = new int[k]; + int[][] dp1 = getdp(nums1); // 生成dp1这个表,以后从nums1中,只要固定拿N个数, + int[][] dp2 = getdp(nums2); + for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) { + // arr1 挑 get1个,怎么得到一个最优结果 + int[] pick1 = maxPick(nums1, dp1, get1); + int[] pick2 = maxPick(nums2, dp2, k - get1); + int[] merge = merge(pick1, pick2); + res = preMoreThanLast(res, 0, merge, 0) ? res : merge; + } + return res; + } + + public static int[] merge(int[] nums1, int[] nums2) { + int k = nums1.length + nums2.length; + int[] ans = new int[k]; + for (int i = 0, j = 0, r = 0; r < k; ++r) { + ans[r] = preMoreThanLast(nums1, i, nums2, j) ? nums1[i++] : nums2[j++]; + } + return ans; + } + + public static boolean preMoreThanLast(int[] nums1, int i, int[] nums2, int j) { + while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) { + i++; + j++; + } + return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]); + } + + public static int[] maxNumber2(int[] nums1, int[] nums2, int k) { + int len1 = nums1.length; + int len2 = nums2.length; + if (k < 0 || k > len1 + len2) { + return null; + } + int[] res = new int[k]; + int[][] dp1 = getdp(nums1); + int[][] dp2 = getdp(nums2); + for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) { + int[] pick1 = maxPick(nums1, dp1, get1); + int[] pick2 = maxPick(nums2, dp2, k - get1); + int[] merge = mergeBySuffixArray(pick1, pick2); + res = moreThan(res, merge) ? res : merge; + } + return res; + } + + public static boolean moreThan(int[] pre, int[] last) { + int i = 0; + int j = 0; + while (i < pre.length && j < last.length && pre[i] == last[j]) { + i++; + j++; + } + return j == last.length || (i < pre.length && pre[i] > last[j]); + } + + public static int[] mergeBySuffixArray(int[] nums1, int[] nums2) { + int size1 = nums1.length; + int size2 = nums2.length; + int[] nums = new int[size1 + 1 + size2]; + for (int i = 0; i < size1; i++) { + nums[i] = nums1[i] + 2; + } + nums[size1] = 1; + for (int j = 0; j < size2; j++) { + nums[j + size1 + 1] = nums2[j] + 2; + } + DC3 dc3 = new DC3(nums, 11); + int[] rank = dc3.rank; + int[] ans = new int[size1 + size2]; + int i = 0; + int j = 0; + int r = 0; + while (i < size1 && j < size2) { + ans[r++] = rank[i] > rank[j + size1 + 1] ? nums1[i++] : nums2[j++]; + } + while (i < size1) { + ans[r++] = nums1[i++]; + } + while (j < size2) { + ans[r++] = nums2[j++]; + } + return ans; + } + + public static class DC3 { + + public int[] sa; + public int[] rank; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i + 1; + } + return ans; + } + + } + + public static int[][] getdp(int[] arr) { + int size = arr.length; // 0~N-1 + int pick = arr.length + 1; // 1 ~ N + int[][] dp = new int[size][pick]; + // get 不从0开始,因为拿0个无意义 + // get 1 + for (int get = 1; get < pick; get++) { // 1 ~ N + int maxIndex = size - get; + // i~N-1 + for (int i = size - get; i >= 0; i--) { + if (arr[i] >= arr[maxIndex]) { + maxIndex = i; + } + dp[i][get] = maxIndex; + } + } + return dp; + } + + public static int[] maxPick(int[] arr, int[][] dp, int pick) { + int[] res = new int[pick]; + for (int resIndex = 0, dpRow = 0; pick > 0; pick--, resIndex++) { + res[resIndex] = arr[dp[dpRow][pick]]; + dpRow = dp[dpRow][pick] + 1; + } + return res; + } + +} diff --git a/src/class44/Code02_LastSubstringInLexicographicalOrder.java b/src/class44/Code02_LastSubstringInLexicographicalOrder.java new file mode 100644 index 0000000..186fb06 --- /dev/null +++ b/src/class44/Code02_LastSubstringInLexicographicalOrder.java @@ -0,0 +1,148 @@ +package class44; + +// 测试链接: https://leetcode.com/problems/last-substring-in-lexicographical-order/ +public class Code02_LastSubstringInLexicographicalOrder { + + public static String lastSubstring(String s) { + if (s == null || s.length() == 0) { + return s; + } + int N = s.length(); + char[] str = s.toCharArray(); + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (char cha : str) { + min = Math.min(min, cha); + max = Math.max(max, cha); + } + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = str[i] - min + 1; + } + DC3 dc3 = new DC3(arr, max - min + 1); + return s.substring(dc3.sa[N - 1]); + } + + public static class DC3 { + + public int[] sa; + + public int[] rank; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i + 1; + } + return ans; + } + + } + +} diff --git a/src/class44/Code03_InsertS2MakeMostAlphabeticalOrder.java b/src/class44/Code03_InsertS2MakeMostAlphabeticalOrder.java new file mode 100644 index 0000000..f533492 --- /dev/null +++ b/src/class44/Code03_InsertS2MakeMostAlphabeticalOrder.java @@ -0,0 +1,249 @@ +package class44; + +public class Code03_InsertS2MakeMostAlphabeticalOrder { + + // 暴力方法 + public static String right(String s1, String s2) { + if (s1 == null || s1.length() == 0) { + return s2; + } + if (s2 == null || s2.length() == 0) { + return s1; + } + String p1 = s1 + s2; + String p2 = s2 + s1; + String ans = p1.compareTo(p2) > 0 ? p1 : p2; + for (int end = 1; end < s1.length(); end++) { + String cur = s1.substring(0, end) + s2 + s1.substring(end); + if (cur.compareTo(ans) > 0) { + ans = cur; + } + } + return ans; + } + + // 正式方法 + public static String max(String s1, String s2) { + if (s1 == null || s1.length() == 0) { + return s2; + } + if (s2 == null || s2.length() == 0) { + return s1; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length; + int M = str2.length; + int min = str1[0]; + int max = str1[0]; + for (int i = 1; i < N; i++) { + min = Math.min(min, str1[i]); + max = Math.max(max, str1[i]); + } + for (int i = 0; i < M; i++) { + min = Math.min(min, str2[i]); + max = Math.max(max, str2[i]); + } + int[] all = new int[N + M + 1]; + int index = 0; + for (int i = 0; i < N; i++) { + all[index++] = str1[i] - min + 2; + } + all[index++] = 1; + for (int i = 0; i < M; i++) { + all[index++] = str2[i] - min + 2; + } + DC3 dc3 = new DC3(all, max - min + 2); + int[] rank = dc3.rank; + int comp = N + 1; + for (int i = 0; i < N; i++) { + if (rank[i] < rank[comp]) { + int best = whereSplit(s1, s2, i); + return s1.substring(0, best) + s2 + s1.substring(best); + } + } + return s1 + s2; + } + + public static class DC3 { + + public int[] sa; + public int[] rank; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i + 1; + } + return ans; + } + + } + + public static int whereSplit(String str1, String str2, int first) { + int M = str2.length(); + String bestPrefix = str2; + int bestSplit = first; + for (int i = first + 1, j = M - 1; i <= Math.min(str1.length(), first + M); i++, j--) { + String curPrefix = str1.substring(first, i) + str2.substring(0, j); + if (curPrefix.compareTo(bestPrefix) >= 0) { + bestPrefix = curPrefix; + bestSplit = i; + } + } + return bestSplit; + } + + // for test + public static String randomNumberString(int len, int range) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * range) + '0'); + } + return String.valueOf(str); + } + + // for test + public static void main(String[] args) { + int range = 10; + int len = 50; + int testTime = 100000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int s1Len = (int) (Math.random() * len); + int s2Len = (int) (Math.random() * len); + String s1 = randomNumberString(s1Len, range); + String s2 = randomNumberString(s2Len, range); + String ans1 = right(s1, s2); + String ans2 = max(s1, s2); + if (!ans1.equals(ans2)) { + System.out.println("Oops!"); + System.out.println(s1); + System.out.println(s2); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + System.out.println("=========="); + System.out.println("性能测试开始"); + + int s1Len = 1000000; + int s2Len = 50; + String s1 = randomNumberString(s1Len, range); + String s2 = randomNumberString(s2Len, range); + long start = System.currentTimeMillis(); + max(s1, s2); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " ms"); + System.out.println("性能测试结束"); + } + +} diff --git a/src/class44/DC3.java b/src/class44/DC3.java new file mode 100644 index 0000000..6a43e3b --- /dev/null +++ b/src/class44/DC3.java @@ -0,0 +1,123 @@ +package class44; + +public class DC3 { + + public int[] sa; + + public int[] rank; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i + 1; + } + return ans; + } + +} \ No newline at end of file diff --git a/src/class44/DC3_Algorithm.pdf b/src/class44/DC3_Algorithm.pdf new file mode 100644 index 0000000..f053a67 Binary files /dev/null and b/src/class44/DC3_Algorithm.pdf differ diff --git a/src/class45/Code01_BurstBalloons.java b/src/class45/Code01_BurstBalloons.java new file mode 100644 index 0000000..004fd4d --- /dev/null +++ b/src/class45/Code01_BurstBalloons.java @@ -0,0 +1,85 @@ +package class45; + +public class Code01_BurstBalloons { + + 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]; + System.out.println(dp[i][i]); + } + for (int L = N; L >= 1; L--) { + for (int R = L + 1; R <= N; R++) { + // 求解dp[L][R],表示help[L..R]上打爆所有气球的最大分数 + // 最后打爆help[L]的方案 + int finalL = help[L - 1] * help[L] * help[R + 1] + dp[L + 1][R]; + // 最后打爆help[R]的方案 + int finalR = help[L - 1] * help[R] * help[R + 1] + dp[L][R - 1]; + // 最后打爆help[L]的方案,和最后打爆help[R]的方案,先比较一下 + dp[L][R] = Math.max(finalL, finalR); + // 尝试中间位置的气球最后被打爆的每一种方案 + for (int i = L + 1; i < R; i++) { + dp[L][R] = Math.max(dp[L][R], help[L - 1] * help[i] + * help[R + 1] + dp[L][i - 1] + dp[i + 1][R]); + } + } + } + return dp[1][N]; + } + + public static void main(String[] args) { + int[] arr = { 4, 2, 3, 5, 1, 6 }; + System.out.println(maxCoins1(arr)); + System.out.println(maxCoins2(arr)); + } + +} diff --git a/src/class45/Code02_RemoveBoxes.java b/src/class45/Code02_RemoveBoxes.java new file mode 100644 index 0000000..6a9f2f8 --- /dev/null +++ b/src/class45/Code02_RemoveBoxes.java @@ -0,0 +1,38 @@ +package class45; + +public class Code02_RemoveBoxes { + + public int removeBoxes(int[] boxes) { + int N = boxes.length; + int[][][] dp = new int[N][N][N]; + return process(boxes, 0, N - 1, 0, dp); + } + + // boxes[L....R],前面还跟着K个boxes[L] + // 前面的包袱和L...R所有的数都消掉,最好得分是什么 + public static int process(int[] boxes, int L, int R, int K, int[][][] dp) { + if (L > R) { + return 0; + } + if (dp[L][R][K] != 0) { + return dp[L][R][K]; + } + if (L == R) { + dp[L][R][K] = (K + 1) * (K + 1); + return dp[L][R][K]; + } + while (L < R && boxes[L] == boxes[L + 1]) { + L++; + K++; + } + int ans = (K + 1) * (K + 1) + process(boxes, L + 1, R, 0, dp); + for (int m = L + 1; m <= R; m++) { + if (boxes[L] == boxes[m]) { + ans = Math.max(ans, process(boxes, L + 1, m - 1, 0, dp) + process(boxes, m, R, K + 1, dp)); + } + } + dp[L][R][K] = ans; + return ans; + } + +} diff --git a/src/class45/Code03_StrangePrinter.java b/src/class45/Code03_StrangePrinter.java new file mode 100644 index 0000000..cfcf7ca --- /dev/null +++ b/src/class45/Code03_StrangePrinter.java @@ -0,0 +1,33 @@ +package class45; + +public class Code03_StrangePrinter { + + public static int strangePrinter(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] ? 1 : 2; + } + for (int L = N - 3; L >= 0; L--) { + for (int R = L + 2; R < N; R++) { + + // L....R + + dp[L][R] = R - L + 1; + + // L...k-1 k...R + for (int k = L + 1; k <= R; k++) { + dp[L][R] = Math.min(dp[L][R], dp[L][k - 1] + dp[k][R] - (str[L] == str[k] ? 1 : 0)); + } + } + } + return dp[0][N - 1]; + } + +} diff --git a/src/class46/Code01_RestoreWays.java b/src/class46/Code01_RestoreWays.java new file mode 100644 index 0000000..a4d913e --- /dev/null +++ b/src/class46/Code01_RestoreWays.java @@ -0,0 +1,237 @@ +package class46; + +public class Code01_RestoreWays { + + /* + * 整型数组arr长度为n(3 <= n <= 10^4),最初每个数字是<=200的正数且满足如下条件: 1. 0位置的要求:arr[0] <= + * arr[1] 2. n-1位置的要求:arr[n-1] <= arr[n-2] 3. 中间i位置的要求:arr[i] <= max(arr[i-1], + * arr[i+1]) 但是在arr有些数字丢失了,比如k位置的数字之前是正数,丢失之后k位置的数字为0。 请你根据上述条件, + * 计算可能有多少种不同的arr可以满足以上条件。 比如 [6,0,9] 只有还原成 [6,9,9]满足全部三个条件,所以返回1种。 [6,9,9] 达标 + * + * [ ...... 0 0] + */ + + public static int ways0(int[] arr) { + return process0(arr, 0); + } + + public static int process0(int[] arr, int index) { + if (index == arr.length) { + return isValid(arr) ? 1 : 0; + } else { + if (arr[index] != 0) { + return process0(arr, index + 1); + } else { + int ways = 0; + for (int v = 1; v < 201; v++) { + arr[index] = v; + ways += process0(arr, index + 1); + } + arr[index] = 0; + return ways; + } + } + } + + public static boolean isValid(int[] arr) { + if (arr[0] > arr[1]) { + return false; + } + if (arr[arr.length - 1] > arr[arr.length - 2]) { + return false; + } + for (int i = 1; i < arr.length - 1; i++) { + if (arr[i] > Math.max(arr[i - 1], arr[i + 1])) { + return false; + } + } + return true; + } + + public static int ways1(int[] arr) { + int N = arr.length; + if (arr[N - 1] != 0) { + return process1(arr, N - 1, arr[N - 1], 2); + } else { + int ways = 0; + for (int v = 1; v < 201; v++) { + ways += process1(arr, N - 1, v, 2); + } + return ways; + } + } + + // 如果i位置的数字变成了v, + // 并且arr[i]和arr[i+1]的关系为s, + // s==0,代表arr[i] < arr[i+1] 右大 + // s==1,代表arr[i] == arr[i+1] + // s==2,代表arr[i] > arr[i+1] 右小 + // 返回0...i范围上有多少种有效的转化方式? + public static int process1(int[] arr, int i, int v, int s) { + if (i == 0) { // 0...i 只剩一个数了,0...0 + return ((s == 0 || s == 1) && (arr[i] == 0 || v == arr[i])) ? 1 : 0; + } + // i > 0 + if (arr[i] != 0 && v != arr[i]) { + return 0; + } + // i>0 并且 i位置的数真的可以变成V, + int ways = 0; + if (s == 0 || s == 1) { // [i - 1] [i] <= 右 + for (int pre = 1; pre < 201; pre++) { + ways += process1(arr, i - 1, pre, pre < v ? 0 : (pre == v ? 1 : 2)); + } + } else { // s == 2 i > 右 i-1 >= i > 右 + for (int pre = v; pre < 201; pre++) { + ways += process1(arr, i - 1, pre, pre == v ? 1 : 2); + } + } + return ways; + } + + public static int ways2(int[] arr) { + int N = arr.length; + int[][][] dp = new int[N][201][3]; + // dp[0][...][...] + // dp[0][...][2] = 0 + // dp[0][...][0] = 1 + // dp[0][...][1] = 1 + if (arr[0] != 0) { + dp[0][arr[0]][0] = 1; + dp[0][arr[0]][1] = 1; + } else { + for (int v = 1; v < 201; v++) { + dp[0][v][0] = 1; + dp[0][v][1] = 1; + } + } + for (int i = 1; i < N; i++) { + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + if (arr[i] == 0 || v == arr[i]) { + if (s == 0 || s == 1) { + for (int pre = 1; pre < 201; pre++) { + dp[i][v][s] += dp[i - 1][pre][pre < v ? 0 : (pre == v ? 1 : 2)]; + } + } else { + for (int pre = v; pre < 201; pre++) { + dp[i][v][s] += dp[i - 1][pre][pre < v ? 0 : (pre == v ? 1 : 2)]; + } + } + } + } + } + } + if (arr[N - 1] != 0) { + return dp[N - 1][arr[N - 1]][2]; + } else { + int ways = 0; + for (int v = 1; v < 201; v++) { + ways += dp[N - 1][v][2]; + } + return ways; + } + } + + public static int ways3(int[] arr) { + int N = arr.length; + int[][][] dp = new int[N][201][3]; + if (arr[0] != 0) { + dp[0][arr[0]][0] = 1; + dp[0][arr[0]][1] = 1; + } else { + for (int v = 1; v < 201; v++) { + dp[0][v][0] = 1; + dp[0][v][1] = 1; + } + } + // presum[0~V][0] -> sum0[] + // presum[0~V][1] -> sum1[] + // presum[0~V][2] -> sum2[] + int[][] presum = new int[201][3]; + // presum -> dp[0][..][..] 三张表 + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + presum[v][s] = presum[v - 1][s] + dp[0][v][s]; + } + } + for (int i = 1; i < N; i++) { + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + if (arr[i] == 0 || v == arr[i]) { + if (s == 0 || s == 1) { + // dp[i][..][..] -> dp[i-1][..][..] + dp[i][v][s] += sum(1, v - 1, 0, presum); + dp[i][v][s] += dp[i - 1][v][1]; + dp[i][v][s] += sum(v + 1, 200, 2, presum); + } else { + dp[i][v][s] += dp[i - 1][v][1]; + dp[i][v][s] += sum(v + 1, 200, 2, presum); + } + } + } + } + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + presum[v][s] = presum[v - 1][s] + dp[i][v][s]; + } + } + } + if (arr[N - 1] != 0) { + return dp[N - 1][arr[N - 1]][2]; + } else { + return sum(1, 200, 2, presum); + } + } + + public static int sum(int begin, int end, int relation, int[][] presum) { + return presum[end][relation] - presum[begin - 1][relation]; + } + + // for test + public static int[] generateRandomArray(int len) { + int[] ans = new int[(int) (Math.random() * len) + 2]; + for (int i = 0; i < ans.length; i++) { + if (Math.random() < 0.5) { + ans[i] = 0; + } else { + ans[i] = (int) (Math.random() * 200) + 1; + } + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + System.out.println("arr size : " + arr.length); + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 3; + int testTime = 10; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(len); + int ans0 = ways0(arr); + int ans1 = ways1(arr); + int ans2 = ways2(arr); + int ans3 = ways3(arr); + if (ans0 != ans1 || ans2 != ans3 || ans0 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + int[] arr = generateRandomArray(100000); + System.out.println(arr.length); + long begin = System.currentTimeMillis(); + ways3(arr); + long end = System.currentTimeMillis(); + System.out.println("run time : " + (end - begin) + " ms"); + + } + +} diff --git a/src/class46/Code02_DeleteAdjacentSameCharacter.java b/src/class46/Code02_DeleteAdjacentSameCharacter.java new file mode 100644 index 0000000..bc94ccb --- /dev/null +++ b/src/class46/Code02_DeleteAdjacentSameCharacter.java @@ -0,0 +1,172 @@ +package class46; + +// 如果一个字符相邻的位置没有相同字符,那么这个位置的字符出现不能被消掉 +// 比如:"ab",其中a和b都不能被消掉 +// 如果一个字符相邻的位置有相同字符,就可以一起消掉 +// 比如:"abbbc",中间一串的b是可以被消掉的,消除之后剩下"ac" +// 某些字符如果消掉了,剩下的字符认为重新靠在一起 +// 给定一个字符串,你可以决定每一步消除的顺序,目标是请尽可能多的消掉字符,返回最少的剩余字符数量 +// 比如:"aacca", 如果先消掉最左侧的"aa",那么将剩下"cca",然后把"cc"消掉,剩下的"a"将无法再消除,返回1 +// 但是如果先消掉中间的"cc",那么将剩下"aaa",最后都消掉就一个字符也不剩了,返回0,这才是最优解。 +// 再比如:"baaccabb", +// 如果先消除最左侧的两个a,剩下"bccabb", +// 如果再消除最左侧的两个c,剩下"babb", +// 最后消除最右侧的两个b,剩下"ba"无法再消除,返回2 +// 而最优策略是: +// 如果先消除中间的两个c,剩下"baaabb", +// 如果再消除中间的三个a,剩下"bbb", +// 最后消除三个b,不留下任何字符,返回0,这才是最优解 +public class Code02_DeleteAdjacentSameCharacter { + + // 暴力解 + public static int restMin1(String s) { + if (s == null) { + return 0; + } + if (s.length() < 2) { + return s.length(); + } + int minLen = s.length(); + for (int L = 0; L < s.length(); L++) { + for (int R = L + 1; R < s.length(); R++) { + if (canDelete(s.substring(L, R + 1))) { + minLen = Math.min(minLen, restMin1(s.substring(0, L) + s.substring(R + 1, s.length()))); + } + } + } + return minLen; + } + + public static boolean canDelete(String s) { + char[] str = s.toCharArray(); + for (int i = 1; i < str.length; i++) { + if (str[i - 1] != str[i]) { + return false; + } + } + return true; + } + + // 优良尝试的暴力递归版本 + public static int restMin2(String s) { + if (s == null) { + return 0; + } + if (s.length() < 2) { + return s.length(); + } + char[] str = s.toCharArray(); + return process(str, 0, str.length - 1, false); + } + + public static int process(char[] str, int L, int R, boolean has) { + if (L > R) { + return 0; + } + if (L == R) { + return has ? 0 : 1; + } + int index = L; + int K = has ? 1 : 0; + while (index <= R && str[index] == str[L]) { + K++; + index++; + } + int way1 = (K > 1 ? 0 : 1) + process(str, index, R, false); + int way2 = Integer.MAX_VALUE; + for (int split = index; split <= R; split++) { + if (str[split] == str[L] && str[split] != str[split - 1]) { + if (process(str, index, split - 1, false) == 0) { + way2 = Math.min(way2, process(str, split, R, K != 0)); + } + } + } + return Math.min(way1, way2); + } + + // 优良尝试的动态规划版本 + public static int restMin3(String s) { + if (s == null) { + return 0; + } + if (s.length() < 2) { + return s.length(); + } + char[] str = s.toCharArray(); + int N = str.length; + int[][][] dp = new int[N][N][2]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + for (int k = 0; k < 2; k++) { + dp[i][j][k] = -1; + } + } + } + return dpProcess(str, 0, N - 1, false, dp); + } + + public static int dpProcess(char[] str, int L, int R, boolean has, int[][][] dp) { + if (L > R) { + return 0; + } + int K = has ? 1 : 0; + if (dp[L][R][K] != -1) { + return dp[L][R][K]; + } + int ans = 0; + if (L == R) { + ans = (K == 0 ? 1 : 0); + } else { + int index = L; + int all = K; + while (index <= R && str[index] == str[L]) { + all++; + index++; + } + int way1 = (all > 1 ? 0 : 1) + dpProcess(str, index, R, false, dp); + int way2 = Integer.MAX_VALUE; + for (int split = index; split <= R; split++) { + if (str[split] == str[L] && str[split] != str[split - 1]) { + if (dpProcess(str, index, split - 1, false, dp) == 0) { + way2 = Math.min(way2, dpProcess(str, split, R, all > 0, dp)); + } + } + } + ans = Math.min(way1, way2); + } + dp[L][R][K] = ans; + return ans; + } + + public static String randomString(int len, int variety) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * variety) + 'a'); + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int maxLen = 16; + int variety = 3; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * maxLen); + String str = randomString(len, variety); + int ans1 = restMin1(str); + int ans2 = restMin2(str); + int ans3 = restMin3(str); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println(str); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +}