题目一:前缀树
一个字符串类型的数组arr1,另一个字符串类型的数组arr2。
1 arr2中有哪些字符,是arr1中出现的?
2 arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?
3 arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印 arr2中出现次数最大的前缀
这些类型的题目都可以用前缀树解决
前缀树的定义和常用方法代码:
public static class TrieNode { public int path; public int end; public TrieNode[] nexts; public TrieNode() { path = 0; end = 0; nexts = new TrieNode[26]; } } public static class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public void insert(String word) { if (word == null) { return; } char[] chs = word.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (node.nexts[index] == null) { node.nexts[index] = new TrieNode(); } node = node.nexts[index]; node.path++; } node.end++; } public void delete(String word) { if (search(word) != 0) { char[] chs = word.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (--node.nexts[index].path == 0) { node.nexts[index] = null; return; } node = node.nexts[index]; } node.end--; } } public int search(String word) { if (word == null) { return 0; } char[] chs = word.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (node.nexts[index] == null) { return 0; } node = node.nexts[index]; } return node.end; } public int prefixNumber(String pre) { if (pre == null) { return 0; } char[] chs = pre.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (node.nexts[index] == null) { return 0; } node = node.nexts[index]; } return node.path; } }
题目二:
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如 长度为20的 金条,不管切成长度多大的两半,都要花费20个铜 板。一群人想整分整块金 条,怎么分最省铜板? 例如,给定数组{10,20,30},代表一共三个人,整块金条长度为 10+20+30=60. 金条要分成10,20,30三个部分。 如果, 先把长 度60的金条分成10和50,花费60 再把长度50的金条分成20和30, 花费50 一共花费110铜板。
但是如果, 先把长度60的金条分成30和30,花费60 再把长度30 金条分成10和20,花费30 一共花费90铜板。 输入一个数组,返回分割的最小代价。
贪心思想:
基于哈夫曼编码的思想,
(1) 首先构造小根堆
(2) 每次取最小的两个数(小根堆),使其代价最小。并将其和加入到小根堆中
(3) 重复(2)过程,直到最后堆中只剩下一个节点
注意:代价不是最后一个值,而是所有非叶结点之和,即上面求得两两个结点之和
public static int lessMoney(int[] arr) { PriorityQueue<Integer> pQ = new PriorityQueue<>(); for (int i = 0; i < arr.length; i++) { pQ.add(arr[i]); } int sum = 0; int cur = 0; while (pQ.size() > 1) { cur = pQ.poll() + pQ.poll(); sum += cur; pQ.add(cur); } return sum; }
题目三:
项目最大钱数
输入: 参数1:正数数组costs ,参数2:正数数组profits ,参数3:正数k, 参数4:正数W
costs[i]表示i号项目的花费, profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润),k表示你不能并行、只能串行的最多做k个项目,m表示你初始的资金
说明:你每做完一个项目,马上获得的收益,可以支持你去做下一个项目。
输出: 你最后获得的最大钱数。
贪心思想:
(1) 首先根据 每个项目的花费构造小根堆
(2)从小跟堆中取出所有小于W的项目,加入 以每个项目的利润构造的大根堆
(3)每次从大根堆中取出一个项目,然后W = W+profits[x] ,之后继续从小根堆取出花费小于W的项目加入大根堆,以此类推
(4)程序终止条件为 做完K的项目 或者 大根堆为空(说明小根堆中已经没有花费小于W的项目了)
代码:
public class IPO { public static class Node { public int p; public int c; public Node(int p, int c) { this.p = p; this.c = c; } } public static class MinCostComparator implements Comparator<Node> { @Override public int compare(Node o1, Node o2) { return o1.c - o2.c; } } public static class MaxProfitComparator implements Comparator<Node> { @Override public int compare(Node o1, Node o2) { return o2.p - o1.p; } } public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) { Node[] nodes = new Node[Profits.length]; for (int i = 0; i < Profits.length; i++) { nodes[i] = new Node(Profits[i], Capital[i]); } PriorityQueue<Node> minCostQ = new PriorityQueue<>(new MinCostComparator()); PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator()); for (int i = 0; i < nodes.length; i++) { minCostQ.add(nodes[i]); } for (int i = 0; i < k; i++) { while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) { maxProfitQ.add(minCostQ.poll()); } if (maxProfitQ.isEmpty()) { return W; } W += maxProfitQ.poll().p; } return W; } }
题目四:
错误的贪心策略:每次比较 两个字符串,字典序小的放在前,大的放在后。
这样不一定对,比如 b 和 ba ,b<ba,按照这种贪心策略,排序结果为 bba,但是其实最小的顺序应该为bab。
正确的贪心策略:每次比较两个字符串和的大小,比如 b 和 ba,bba> bab ,所以ba应该放在b的前面
代码:
public class LowestLexicography { public static class MyComparator implements Comparator<String> { @Override public int compare(String a, String b) { return (a + b).compareTo(b + a); } } public static String lowestString(String[] strs) { if (strs == null || strs.length == 0) { return ""; } Arrays.sort(strs, new MyComparator()); String res = ""; for (int i = 0; i < strs.length; i++) { res += strs[i]; } return res; } public static void main(String[] args) { String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" }; System.out.println(lowestString(strs1)); String[] strs2 = { "ba", "b" }; System.out.println(lowestString(strs2)); } }
题目五:
贪心策略:
每次找结束时间最早的会议,也就是首先按照结束时间早晚排序,找到一个最早结束的会议,场次加一,然后比较第二早结束的会议的开始时间
是否 大于 上一场 的结束时间,如果不大于,忽略掉,看下一场会议,如果大于,场次加一,然后看下一场会议是否满足条件,以此类推。
代码:
public static class Program { public int start; public int end; public Program(int start, int end) { this.start = start; this.end = end; } } public static class ProgramComparator implements Comparator<Program> { @Override public int compare(Program o1, Program o2) { return o1.end - o2.end; } } //start初始值可以为-1 public static int bestArrange(Program[] programs, int start) { Arrays.sort(programs, new ProgramComparator()); int result = 0; for (int i = 0; i < programs.length; i++) { if (start <= programs[i].start) { result++; start = programs[i].end; } } return result; }