随时补充 ----
Remove Substrings
Given a string s and a set of n substrings. You are supposed to remove every instance of those n substrings from s so that s is of the minimum length and output this minimum length. Have you met this question in a real interview? Yes Example Given s = ccdaabcdbb, substrs = ["ab", "cd"] Return 2 Explanation: ccdaabcdbb -> ccdacdbb -> cabb -> cb (length = 2)
思路:很容易想到贪心,能尽量削减原串就削减原串,但是贪心是错误的,反例:"abcabd", ["ab","abcd"]
用DFS,对于dict中的每一个子串,在原串中找到匹配的该串的索引,并截取原字符串,更新结果,将截取后的字符串加入到队列中(增加一个set来避免相同的串重复加入到队列)以便下一次循环,然后从该索引后面的一个位置开始再找与该子串匹配的索引,重复上述过程。
1 public class Solution { 2 /** 3 * @param s a string 4 * @param dict a set of n substrings 5 * @return the minimum length 6 */ 7 public int minLength(String s, Set<String> dict) { 8 // Write your code here 9 if (s == null) { 10 return 0; 11 } 12 if (s.length() == 0 || dict == null || dict.size() == 0) { 13 return s.length(); 14 } 15 int result = s.length(); 16 Queue<String> queue = new LinkedList<>(); 17 Set<String> set = new HashSet<>(); 18 queue.offer(s); 19 set.add(s); 20 while (!queue.isEmpty()) { 21 String str = queue.poll(); 22 for (String subStr : dict) { 23 int index = str.indexOf(subStr); 24 while (index != -1) { 25 String newStr = str.substring(0, index) 26 + str.substring(index + subStr.length(), str.length()); 27 if (!set.contains(newStr)) { 28 set.add(newStr); 29 queue.offer(newStr); 30 result = Math.min(result, newStr.length()); 31 } 32 index = str.indexOf(subStr, index + 1); 33 } 34 } 35 } 36 return result; 37 } 38 }
Permutation Index
Given a permutation which contains no repeated number, find its index in all the permutations of these numbers, which are ordered in lexicographical order. The index begins at 1. Have you met this question in a real interview? Yes Example Given [1,2,4], return 1.
思路:
对于某一个给定的位数A[i],需要判断在它后面有几个小于它的数,记下这个数字和A[i]所在的位置。
比如对于一个四位数,5316 , 第一位后面有2小于它的数,如果这两个数排在第一位,那么(1和3)各有3!的排列组合数小于(5316).
同理,对于第二位,其后有1个小于它的数,如果它放在第二位,那么有2!种排列。
因此判断一个给定数位于排列组合的第几位,则可以按照以下公式进行
count1*(A.length-1)!+count2*(A.length-2)!+......+countn*(0)!
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @return a long integer 5 */ 6 public long permutationIndex(int[] A) { 7 // Write your code here 8 if (A == null || A.length == 0) { 9 return 0; 10 } 11 long result = 1; 12 long factor = 1; 13 for (int i = A.length - 1; i >= 0; i--) { 14 int count = 0; 15 for (int j = i + 1; j < A.length; j++) { 16 if (A[j] < A[i]) { 17 count++; 18 } 19 } 20 result += count * factor; 21 factor *= A.length - i; 22 } 23 return result; 24 } 25 }
Permutation Index II
Given a permutation which may contain repeated numbers, find its index in all the permutations of these numbers, which are ordered in lexicographical order. The index begins at 1. Have you met this question in a real interview? Yes Example Given the permutation [1, 4, 2, 2], return 3.
思路:
这道题和Permutation IndexI思想一样,计算每一位上数字是该位上第几个排列,再将每一位结果加和即可。只是这道题有重复元素,有无重复元素最大的区别在于原来的1!, 2!, 3!...等需要除以重复元素个数的阶乘。按照数字从低位到高位进行计算。每遇到一个重复的数字就更新重复元素个数的阶乘的值。
-
从后往前遍历数组,用一个hashmap来记录重复元素个数。若新来的数不是重复元素,则加入hashmap,否则将重复元素个数+1,同时更新重复元素个数的阶乘。
-
比较当前位和其后面位的数,计算当前位是第几大的数count
-
当前位的index为:2的结果count * 其后面位数的阶乘/重复数个数的阶乘。将当前位计入阶乘,重复1-3计算前一位。
- 注意:1.题目说index从1开始算。2.要用long来保存result,factor和repeatFactor,用int有可能超过范围
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @return a long integer 5 */ 6 public long permutationIndexII(int[] A) { 7 // Write your code here 8 if (A == null || A.length == 0) { 9 return 0; 10 } 11 long result = 1; 12 long factor = 1; 13 long repeatFactor = 1; 14 Map<Integer, Integer> map = new HashMap<>(); 15 for (int i = A.length - 1; i >= 0; i--) { 16 if (!map.containsKey(A[i])) { 17 map.put(A[i], 1); 18 } else { 19 map.put(A[i], map.get(A[i]) + 1); 20 repeatFactor *= map.get(A[i]); 21 } 22 int count = 0; 23 for (int j = i + 1; j < A.length; j++) { 24 if (A[j] < A[i]) { 25 count++; 26 } 27 } 28 result += count * factor / repeatFactor; 29 factor *= A.length - i; 30 } 31 return result; 32 } 33 }
Maximum Subarray
Given an array of integers, find a contiguous subarray which has the largest sum. Notice The subarray should contain at least one number. Have you met this question in a real interview? Yes Example Given the array [−2,2,−3,4,−1,2,1,−5,3], the contiguous subarray [4,−1,2,1] has the largest sum = 6.
思路:sum记录到当前索引的子数组的和,minSum记录该索引之前的子数组(从索引0开始)的和的最小值。sum - minSum表示以当前索引结尾的最大子数组的和,遍历一遍后取sum - minSum的最大值即可。
1 public class Solution { 2 /** 3 * @param nums: A list of integers 4 * @return: A integer indicate the sum of max subarray 5 */ 6 public int maxSubArray(int[] nums) { 7 // write your code 8 if (nums == null || nums.length == 0) { 9 return 0; 10 } 11 int minSum = 0; 12 int sum = 0; 13 int max = Integer.MIN_VALUE; 14 for (int i = 0; i < nums.length; i++) { 15 sum += nums[i]; 16 max = Math.max(max, sum - minSum); 17 minSum = Math.min(minSum, sum); 18 } 19 return max; 20 } 21 }
Maximum Subarray II
Given an array of integers, find two non-overlapping subarrays which have the largest sum. The number in each subarray should be contiguous. Return the largest sum. Notice The subarray should contain at least one number Have you met this question in a real interview? Yes Example For given [1, 3, -1, 2, -1, 2], the two subarrays are [1, 3] and [2, -1, 2] or [1, 3, -1, 2] and [2], they both have the largest sum 7.
思路:求两个不重叠的子数组的和最大。因为两个subarray 一定不重叠,所以必定存在一条分割线,分开这两个 subarrays。枚举分割线的位置,left[] 和 right[] 里分别存的是,某个位置往左的 maximum subarray 和往右的 maximum subarray。
1 public class Solution { 2 /** 3 * @param nums: A list of integers 4 * @return: An integer denotes the sum of max two non-overlapping subarrays 5 */ 6 public int maxTwoSubArrays(ArrayList<Integer> nums) { 7 // write your code 8 if (nums == null || nums.size() < 2) { 9 return 0; 10 } 11 int sum = 0; 12 int minSum = 0; 13 int max = Integer.MIN_VALUE; 14 int[] left = new int[nums.size()]; 15 for (int i = 0; i < nums.size(); i++) { 16 sum += nums.get(i); 17 max = Math.max(max, sum - minSum); 18 minSum = Math.min(minSum, sum); 19 left[i] = max; 20 } 21 sum = 0; 22 minSum = 0; 23 max = Integer.MIN_VALUE; 24 int[] right = new int[nums.size()]; 25 for (int i = nums.size() - 1; i >= 0; i--) { 26 sum += nums.get(i); 27 max = Math.max(max, sum - minSum); 28 minSum = Math.min(minSum, sum); 29 right[i] = max; 30 } 31 max = Integer.MIN_VALUE; 32 for (int i = 0; i < nums.size() - 1; i++) { 33 max = Math.max(max, left[i] + right[i + 1]); 34 } 35 return max; 36 } 37 }
Maximum Subarray Difference
Given an array with integers. Find two non-overlapping subarrays A and B, which |SUM(A) - SUM(B)| is the largest. Return the largest difference. Notice The subarray should contain at least one number Have you met this question in a real interview? Yes Example For [1, 2, -3, 1], return 6.
思路:求两个不重叠的子数组的差的绝对值最大。与Maximum Subarray II的区别在于,需要求出left_max、left_min、right_max、right_min这四个数组,分别表示某个位置往左的最大子数组、某个位置往左的最小子数组、某个位置往右的最大子数组、某个位置往右的最小子数组。然后枚举分割线的位置,对于某一位置的分割线,结果是左边最大值与右边最小值的差的绝对值或者左边最小值与右边最大值的差的绝对值。
1 public class Solution { 2 /** 3 * @param nums: A list of integers 4 * @return: An integer indicate the value of maximum difference between two 5 * Subarrays 6 */ 7 public int maxDiffSubArrays(int[] nums) { 8 // write your code here 9 if (nums == null || nums.length <= 1) { 10 return 0; 11 } 12 int n = nums.length; 13 int[] left_max = new int[n]; 14 int[] left_min = new int[n]; 15 int[] right_max = new int[n]; 16 int[] right_min = new int[n]; 17 int max = Integer.MIN_VALUE; 18 int sum = 0; 19 int minSum = 0; 20 for (int i = 0; i < n; i++) { 21 sum += nums[i]; 22 max = Math.max(max, sum - minSum); 23 minSum = Math.min(minSum, sum); 24 left_max[i] = max; 25 } 26 max = Integer.MIN_VALUE; 27 sum = 0; 28 minSum = 0; 29 for (int i = n - 1; i >= 0; i--) { 30 sum += nums[i]; 31 max = Math.max(max, sum - minSum); 32 minSum = Math.min(minSum, sum); 33 right_max[i] = max; 34 } 35 max = Integer.MIN_VALUE; 36 sum = 0; 37 minSum = 0; 38 for (int i = 0; i < n; i++) { 39 sum += -nums[i]; 40 max = Math.max(max, sum - minSum); 41 minSum = Math.min(minSum, sum); 42 left_min[i] = -max; 43 } 44 max = Integer.MIN_VALUE; 45 sum = 0; 46 minSum = 0; 47 for (int i = n - 1; i >= 0; i--) { 48 sum += -nums[i]; 49 max = Math.max(max, sum - minSum); 50 minSum = Math.min(minSum, sum); 51 right_min[i] = -max; 52 } 53 int result = 0; 54 for (int i = 0; i < n - 1; i++) { 55 result = Math.max(result, Math.abs(left_max[i] - right_min[i + 1])); 56 result = Math.max(result, Math.abs(left_min[i] - right_max[i + 1])); 57 } 58 return result; 59 } 60 }
Maximum Subarray III
Given an array of integers and a number k, find k non-overlapping subarrays which have the largest sum. The number in each subarray should be contiguous. Return the largest sum. Notice The subarray should contain at least one number Have you met this question in a real interview? Yes Example Given [-1,4,-2,3,-2,3], k=2, return 8
思路:
local[i][k]表示前i个元素取k个子数组并且必须包含第i个元素的最大和。
global[i][k]表示前i个元素取k个子数组不一定包含第i个元素的最大和。
local[i][k]的状态函数:
max(global[i-1][k-1], local[i-1][k]) + nums[i-1]
有两种情况,第一种是第i个元素自己组成一个子数组,则要在前i-1个元素中找k-1个子数组,第二种情况是第i个元素属于前一个元素的子数组,因此要在i-1个元素中找k个子数组(并且必须包含第i-1个元素,这样第i个元素才能合并到最后一个子数组中),取两种情况里面大的那个。
global[i][k]的状态函数:
max(global[i-1][k],local[i][k])
有两种情况,第一种是不包含第i个元素,所以要在前i-1个元素中找k个子数组,第二种情况为包含第i个元素,在i个元素中找k个子数组且必须包含第i个元素,取两种情况里面大的那个。
注意这一题中DP的方向是先从上往下,再从左往右,而不是通常的先从左往右,再从上往下。这样做是因为localMax[j - 1][j]和globalMax[j - 1][j]置为Integer.MIN_VALUE的次数取决于列循环次数,而不是取决于行循环次数,否则二维数组下标会越界。
1 public class Solution { 2 /** 3 * @param nums: A list of integers 4 * @param k: An integer denote to find k non-overlapping subarrays 5 * @return: An integer denote the sum of max k non-overlapping subarrays 6 */ 7 public int maxSubArray(int[] nums, int k) { 8 // write your code here 9 if (nums == null | nums.length == 0 || k <= 0 || k > nums.length) { 10 return 0; 11 } 12 int n = nums.length; 13 int[][] localMax = new int[n + 1][k + 1]; 14 int[][] globalMax = new int[n + 1][k + 1]; 15 for (int j = 1; j <= k; j++) { 16 //前j-1个元素不可能找到不重叠的j个子数组,因此初始化为最小值 17 //以防后面被取到 18 localMax[j - 1][j] = Integer.MIN_VALUE; 19 globalMax[j - 1][j] = Integer.MIN_VALUE; 20 for (int i = j; i <= n; i++) { 21 localMax[i][j] = Math.max(globalMax[i - 1][j - 1], 22 localMax[i - 1][j]) + nums[i - 1]; 23 globalMax[i][j] = Math.max(globalMax[i - 1][j], localMax[i][j]); 24 } 25 } 26 return globalMax[n][k]; 27 } 28 }
Maximum Subarray IV
Given an integer arrays, find a contiguous subarray which has the largest sum and length should be greater or equal to given length k. Return the largest sum, return 0 if there are fewer than k elements in the array. Notice Ensure that the result is an integer type. Have you met this question in a real interview? Yes Example Given the array [-2,2,-3,4,-1,2,1,-5,3] and k = 5, the contiguous subarray [2,-3,4,-1,2,1] has the largest sum = 5.
思路:同Maximum Subarray,区别在于更新min_pre的时机,不是每次循环都更新,而是i >= 的时候更新,这样才能保证子数组长度大于等于k。
1 public class Solution { 2 /** 3 * @param nums an array of integers 4 * @param k an integer 5 * @return the largest sum 6 */ 7 public int maxSubarray4(int[] nums, int k) { 8 // Write your code here 9 if (nums == null || nums.length == 0 || k > nums.length) { 10 return 0; 11 } 12 int n = nums.length; 13 int[] sum = new int[n + 1]; 14 int min_pre = 0; 15 int result = Integer.MIN_VALUE; 16 for (int i = 1; i <= n; i++) { 17 sum[i] = sum[i - 1] + nums[i - 1]; 18 if (i >= k && sum[i] - min_pre > result) { 19 result = sum[i] - min_pre; 20 } 21 if (i >= k) { 22 min_pre = Math.min(min_pre, sum[i - k + 1]); 23 } 24 } 25 return result; 26 } 27 }
Maximum Subarray V
Given an integer arrays, find a contiguous subarray which has the largest sum and length should be between k1 and k2 (include k1 and k2). Return the largest sum, return 0 if there are fewer than k1 elements in the array. Notice Ensure that the result is an integer type. Have you met this question in a real interview? Yes Example Given the array [-2,2,-3,4,-1,2,1,-5,3] and k1 = 2, k2 = 4, the contiguous subarray [4,-1,2,1] has the largest sum = 6.
思路:使用双端队列Deque,存放i,表示前i个数,第i个数到前index数之间的子数组有可能成为最大和子数组。每次循环,做三个操作:在列首踢掉长度大于k2的子数组的索引,在列尾踢掉子数组的和小于当前长度为k1的子数组的和,此时列首到当前索引的子数组为以当前元素为子数组的最后一个元素的长度在k1 ~ k2之间的最大和子数组,如果这个和比结果大就更新结果。
1 public class Solution { 2 /** 3 * @param nums an array of integers 4 * @param k1 an integer 5 * @param k2 an integer 6 * @return the largest sum 7 */ 8 public int maxSubarray5(int[] nums, int k1, int k2) { 9 // Write your code here 10 if (nums == null || nums.length == 0 || k1 > k2 || k2 <= 0) { 11 return 0; 12 } 13 Deque<Integer> deque = new LinkedList<>(); 14 int n = nums.length; 15 int[] sum = new int[n + 1]; 16 int result = Integer.MIN_VALUE; 17 for (int i = 1; i <= n; i++) { 18 sum[i] = sum[i - 1] + nums[i - 1]; 19 if (!deque.isEmpty() && deque.peekFirst() < i - k2) { 20 deque.pollFirst(); 21 } 22 if (i >= k1) { 23 while (!deque.isEmpty() && sum[i] - sum[deque.peekLast()] < sum[i] - sum[i - k1]) { 24 deque.pollLast(); 25 } 26 deque.offerLast(i - k1); 27 } 28 if (!deque.isEmpty() && sum[i] - sum[deque.peekFirst()] > result) { 29 result = sum[i] - sum[deque.peekFirst()]; 30 } 31 } 32 return result; 33 } 34 }
LRU Cache
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set. get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
思路:HashMap + DoubleLinkedList HashMap存储key -> Node,Node节点里的元素:1. key(可不要) 2. value 3. prev和next指针
这一道题的set操作如果set(key, value)的key已经存在的话,不算做最近使用的。
1 class Node { 2 int key; 3 int value; 4 Node prev; 5 Node next; 6 public Node(int key, int value) { 7 this.key = key; 8 this.value = value; 9 prev = null; 10 next = null; 11 } 12 } 13 14 public class Solution { 15 Node head; 16 Node tail; 17 Map<Integer, Node> map; 18 int capacity; 19 // @param capacity, an integer 20 public Solution(int capacity) { 21 // write your code here 22 head = new Node(-1, -1); 23 tail = new Node(-1, -1); 24 head.next = tail; 25 tail.prev = head; 26 map = new HashMap<Integer, Node>(); 27 this.capacity = capacity; 28 } 29 30 // @return an integer 31 public int get(int key) { 32 // write your code here 33 if (!map.containsKey(key)) { 34 return -1; 35 } 36 Node node = map.get(key); 37 node.prev.next = node.next; 38 node.next.prev = node.prev; 39 move_to_tail(node); 40 return node.value; 41 } 42 43 // @param key, an integer 44 // @param value, an integer 45 // @return nothing 46 public void set(int key, int value) { 47 // write your code here 48 if (get(key) != -1) { 49 map.get(key).value = value; 50 return; 51 } 52 if (map.size() == capacity) { 53 map.remove(head.next.key); 54 head.next = head.next.next; 55 head.next.prev = head; 56 } 57 Node newNode = new Node(key, value); 58 map.put(key, newNode); 59 move_to_tail(newNode); 60 } 61 public void move_to_tail(Node node) { 62 tail.prev.next = node; 63 node.prev = tail.prev; 64 node.next = tail; 65 tail.prev = node; 66 } 67 }
LFU Cache
LFU (Least Frequently Used) is a famous cache eviction algorithm. For a cache with capacity k, if the cache is full and need to evict a key in it, the key with the lease frequently used will be kicked out. Implement set and get method for LFU cache. Have you met this question in a real interview? Yes Example Given capacity=3 set(2,2) set(1,1) get(2) >> 2 get(1) >> 1 get(2) >> 2 set(3,3) set(4,4) get(3) >> -1 get(2) >> 2 get(1) >> 1 get(4) >> 4
思路:双HashMap + DoubleLinkedList。一个valueHash存储:key -> value;另一个nodeHash存储:key -> Node。
Node节点里的元素:1. 频率freq
2. LinkedHashSet<Integer> keys存储freq相同的key集合,可以保证先使用的Node放前面,最近使用的Node放后面
3. prev和next指针
事先构造好虚拟的head和tail Node节点。
注意要随时维护好valueHash、nodeHash和DoubleLinkedList中的元素关系。
1 class Node { 2 int freq; 3 LinkedHashSet<Integer> keys; 4 Node prev; 5 Node next; 6 public Node(int freq) { 7 this.freq = freq; 8 keys = new LinkedHashSet<Integer>(); 9 prev = null; 10 next = null; 11 } 12 } 13 public class LFUCache { 14 int capacity; 15 Node head; 16 Node tail; 17 Map<Integer, Integer> valueHash; 18 Map<Integer, Node> nodeHash; 19 // @param capacity, an integer 20 public LFUCache(int capacity) { 21 // Write your code here 22 this.capacity = capacity; 23 head = new Node(0); 24 tail = new Node(0); 25 head.next = tail; 26 tail.prev = head; 27 valueHash = new HashMap<Integer, Integer>(); 28 nodeHash = new HashMap<Integer, Node>(); 29 } 30 31 // @param key, an integer 32 // @param value, an integer 33 // @return nothing 34 public void set(int key, int value) { 35 // Write your code here 36 if (valueHash.containsKey(key)) { 37 valueHash.put(key, value); 38 increaseFrequency(key); 39 return; 40 } 41 if (valueHash.size() == capacity) { 42 removeOld(); 43 } 44 addToHead(key, value); 45 } 46 47 public int get(int key) { 48 // Write your code here 49 if (!valueHash.containsKey(key)) { 50 return -1; 51 } 52 increaseFrequency(key); 53 return valueHash.get(key); 54 } 55 public void increaseFrequency(int key) { 56 Node node = nodeHash.get(key); 57 node.keys.remove(key); 58 if (node.freq + 1 != node.next.freq) { 59 Node newNode = new Node(node.freq + 1); 60 newNode.keys.add(key); 61 newNode.prev = node; 62 newNode.next = node.next; 63 node.next = newNode; 64 newNode.next.prev = newNode; 65 } else { 66 node.next.keys.add(key); 67 } 68 nodeHash.put(key, node.next); 69 if (node.keys.size() == 0) { 70 remove(node); 71 } 72 } 73 public void remove(Node node) { 74 node.prev.next = node.next; 75 node.next.prev = node.prev; 76 } 77 public void removeOld() { 78 if (head.next == tail) { 79 return; 80 } 81 int index = 0; 82 for (int key : head.next.keys) { 83 index = key; 84 break; 85 } 86 head.next.keys.remove(index); 87 if (head.next.keys.size() == 0) { 88 remove(head.next); 89 } 90 valueHash.remove(index); 91 nodeHash.remove(index); 92 } 93 public void addToHead(int key, int value) { 94 if (head.next.freq != 1) { 95 Node newNode = new Node(1); 96 newNode.keys.add(key); 97 newNode.prev = head; 98 newNode.next = head.next; 99 head.next = newNode; 100 newNode.next.prev = newNode; 101 valueHash.put(key, value); 102 nodeHash.put(key, newNode); 103 } else { 104 head.next.keys.add(key); 105 valueHash.put(key, value); 106 nodeHash.put(key, head.next); 107 } 108 } 109 }