来福软谱

迎难而上,祝我好运。

最小横向刷墙次数

  • 给一个数组A代表糊墙的高度,就是挨在一起的n个矩形,第i个高度为A,宽为1,然后横着刷墙,每一笔高度为1,问最少要多少笔?例如[1,2,2,3,2]->横着4笔
  • one-pass,两两比较,如果后面的比前面的矮,说明刷到这里就会断掉,所以要加上delta。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    private static int minPaint(int[] walls) {
    int count = walls[0];
    for (int i = 1; i < walls.length; i++) {
    if (walls[i] > walls[i - 1]) {
    count += (walls[i] - walls[i - 1]);
    }
    }
    return count;
    }

最少出行天数

  • 给一个数组表示第i天可以去的地点,求去所有出现的地点所需的最短天数。
  • 莉蔻上minmum substring简化版。首先建立一个map,频数都设为1表示我只需要去这个地方至少1天。然后双指针producer-consumer,快指针消耗location直到所有地点都去了(剩余目的地count为0),然后先根据左右慢指针更新minDays,然后开始恢复location计数到map中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    private static int minDays(int[] locations) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int location : locations) {
    map.put(location, 1);
    }
    int locationCount = map.size();
    int left = 0, right = 0, minLen = Integer.MAX_VALUE;
    while (right < locations.length) {
    map.put(locations[right], map.get(locations[right]) - 1);
    if (map.get(locations[right]) == 0) {
    locationCount--;
    }
    while (locationCount == 0) {
    int currLen = right - left + 1;
    minLen = Math.min(minLen, currLen);
    map.put(locations[left], map.get(locations[left]) + 1);
    if (map.get(locations[left]) > 0) {
    locationCount++;
    }
    left++;
    }
    right++;
    }
    return minLen;
    }

分糖果

  • 给一个数组表示糖果的id,其中可能有重复,且糖果数量一定是偶数。要求将其分成两部分,问如果想尝最多的不同的糖果,有多少种。例如[3,2,2,1,3,1]就有三种,[7,3,1,4,3,7,4,3,7]就有四种。要求时间复杂度最差O(N*lgN),空间复杂度O(N)。
  • 先排序,然后双指针一个从前往后,一个从后往前。left指针负责将糖果存入set,right则是调取后方的糖果往前替换,当left发现当前糖果吃过了,就从right那里swap过来继续判断,直到出现新的糖果或者穷尽。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private static int maxCandy(int[] candies) {
    if (candies == null || candies.length == 0) {
    return 0;
    }
    Arrays.sort(candies);
    Set<Integer> set = new HashSet<>();
    int half = candies.length / 2;
    int left = 0, right = candies.length - 1;
    while (left < half) {
    while (set.contains(candies[left]) && right >= half) {
    swap(candies, left, right--);
    }
    if (right < half) {
    break;
    } else {
    set.add(candies[left++]);
    }
    }
    set.add(candies[left]);
    return set.size();
    }

最小无序区间

  • 给一个int数组,求其中最大和最小值恰好为1的非连续子序列。
  • 给一个部分部分有序的数组,求将其中哪一部分排序之后整个数组就都有序了,求最短的区间的长度。
  • 首先从左往右逆序对,然后从右往左找逆序对。这样就有了一个大致区间,但是还需要找区间内的min和max,分别往前和往后遍历看看是否真的完全符合,否则还需要扩展区间。例如[1,3,6,4,8,2,7,10]在前两步之后找到得失[6,4][8,2]实际上前面的3和后面的1都需要加入进来。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    class Solution {
    public int findUnsortedSubarray(int[] nums) {
    if (nums == null || nums.length == 0) {
    return 0;
    }
    // 找第一个逆序对
    int left = 1;
    while (left < nums.length) {
    if (nums[left - 1] > nums[left]) {
    left--;
    break;
    }
    left++;
    }
    if (left == nums.length) { // 说明已经有序了
    return 0;
    }
    int right = nums.length - 1;
    while (right > 0) {
    if (nums[right - 1] > nums[right]) {
    break;
    }
    right--;
    }
    // 找[left, right]之间的最小、最大
    int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
    for (int i = left; i <= right; i++) {
    min = Math.min(min, nums[i]);
    max = Math.max(max, nums[i]);
    }
    // 前面的必须小于min,后面的必须大于max,否则扩散
    while (left > 0 && nums[left - 1] > min) {
    left--;
    }
    while (right + 1 < nums.length && nums[right + 1] < max) {
    right++;
    }
    return right - left + 1;
    }
    }

最长差值恰好为1的子序列

  • 给一个int数组,求其中最大和最小值恰好为1的非连续子序列。
  • 用Map存每个值出现的次数,然后遍历Map取key相差1都存在的进行更新。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Solution {
    public int findLHS(int[] nums) {
    if (nums == null || nums.length == 0) {
    return 0;
    }
    Map<Integer, Integer> map = new HashMap<>();
    for (int num : nums) {
    map.put(num, map.getOrDefault(num, 0) + 1);
    }
    int max = 0;
    for (int num : map.keySet()) {
    if (map.containsKey(num + 1)) {
    max = Math.max(max, map.get(num) + map.get(num + 1));
    }
    }
    return max;
    }
    }

prefix permutation

  • 给一个规模为N的数组,只包含数字1-N,求其中可以作为permutation的子数组的个数。例如[2,1,3,5,4]就有三个:[2,1], [2,1,3], [2,1,3,5,4]
  • 方法一是求和,一旦吻合permutation的sum就加一,但又overflow的风险。
  • 方法二是利用规模为N、数字是1-N这个信息,value和index就有一一对应的关系了(虽然相差1)。只有差值为0的时候才是permutation。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private static int prefixPermutation(int[] arr) {
    int prefixCount = 0, valueIndexDelta = 0;
    for (int i = 0; i < arr.length; i++) {
    valueIndexDelta += (arr[i] - i - 1);
    if (valueIndexDelta == 0) {
    prefixCount++;
    }
    }
    return prefixCount;
    }

扑克牌比大小

  • 给两个相同长度的String表示两个人的牌,含有2-9, J, Q, K, A这些面值,判断第一个人赢的次数。
  • 直接暴力比大小,用一个Map存牌和数字的映射关系,然后直接取就行了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    private static int getWinTimes(String a, String b) {
    if (a == null || b == null || a.length() == 0 || b.length() == 0) {
    return 0;
    }
    Map<Character, Integer> map = new HashMap<>();
    for (int i = 2; i < 10; i++) {
    map.put()
    }
    }