天天看点

LeetCode Weekly Contest 1591232. Check If It Is a Straight Line1233. Remove Sub-Folders from the Filesystem1234. Replace the Substring for Balanced String1235. Maximum Profit in Job Scheduling

1232. Check If It Is a Straight Line

Difficulty:Easy

You are given an array coordinates, coordinates[i] = [x, y], where [x, y] represents the coordinate of a point. Check if these points make a straight line in the XY plane.

Example 1:

Input: coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]

Output: true

Example 2:

Input: coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]]

Output: false

Constraints:

2 <= coordinates.length <= 1000

coordinates[i].length == 2

-10^4 <= coordinates[i][0], coordinates[i][1] <= 10^4

coordinates contains no duplicate point.

题意

判断(x, y)数组是否共线

思路

水平/垂直特殊判断,否则判断斜率,为避免double精度问题,斜率a/b约分上下同除gcd(a,b)之后比较分子分母

代码

class Solution:
    @staticmethod
    def gcd(a, b):
        if b == a:
            return a
        a, b = max(a, b), min(a, b)
        if b == 0:
            return a
        return Solution.gcd(b, a % b)
    
    def checkStraightLine(self, coordinates: List[List[int]]) -> bool:
        coordinates.sort(key=lambda x: x[0])
        n = len(coordinates)
        xeq = yeq = True
        for i in range(n-1):
            if xeq:
                if coordinates[i][0] != coordinates[i+1][0]:
                    xeq = False
            if yeq:
                if coordinates[i][1] != coordinates[i+1][1]:
                    yeq = False
            if not xeq and not yeq:
                break
        if xeq or yeq:
            return True
        px = None
        py = None
        for i in range(n-1):
            x = coordinates[i+1][0] - coordinates[i][0]
            y = abs(coordinates[i+1][1] - coordinates[i][1])
            g = self.gcd(x, y)
            x /= g
            y /= g
            if px != None and py != None and (x != px or y != py):
                return False
            px = x
            py = y
        return True
           

1233. Remove Sub-Folders from the Filesystem

Difficulty:Medium

Given a list of folders, remove all sub-folders in those folders and return in any order the folders after removing.

If a folder[i] is located within another folder[j], it is called a sub-folder of it.

The format of a path is one or more concatenated strings of the form: / followed by one or more lowercase English letters. For example, /leetcode and /leetcode/problems are valid paths while an empty string and / are not.

Example 1:

Input: folder = ["/a","/a/b","/c/d","/c/d/e","/c/f"]

Output: ["/a","/c/d","/c/f"]

Explanation: Folders “/a/b/” is a subfolder of “/a” and “/c/d/e” is inside of folder “/c/d” in our filesystem.

Example 2:

Input: folder = ["/a","/a/b/c","/a/b/d"]

Output: ["/a"]

Explanation: Folders “/a/b/c” and “/a/b/d/” will be removed because they are subfolders of “/a”.

Example 3:

Input: folder = ["/a/b/c","/a/b/ca","/a/b/d"]

Output: ["/a/b/c","/a/b/ca","/a/b/d"]

Constraints:

1 <= folder.length <= 4 * 10^4

2 <= folder[i].length <= 100

folder[i] contains only lowercase letters and ‘/’

folder[i] always starts with character ‘/’

Each folder name is unique.

题意

给定unix目录的数组,如果数组中一个目录是另一个目录的子目录,则去除该子目录,返回去除所有子目录的数组

思路

字典树:节点存储布尔值是否是unix路径结尾,边为目录名。用原数组建树之后dfs返回第一次遇到的路径结尾节点,舍去以某结尾节点为前缀的路径。

代码

class Solution {
    class Trie {
        boolean isEnd;
        HashMap<String, Trie> next;
        
        public Trie() {
            isEnd = false;
            next = new HashMap<String, Trie>();
        }
    }
    
    private void dfs(Trie root, String cur, ArrayList<String> ans) {
        Iterator<Map.Entry<String, Trie>> iter = root.next.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, Trie> entry = iter.next();
            if (entry.getValue().isEnd) {
                ans.add(cur + "/" + entry.getKey());
            } else {
                dfs(entry.getValue(), cur + "/" + entry.getKey(), ans);
            }
        }
    }
    
    public List<String> removeSubfolders(String[] folder) {
        Trie root = new Trie(), ptr = root;
        for (String fold: folder) {
            ptr = root;
            for (String dir: fold.split("/")) {
                if (dir.isEmpty()) {
                    continue;
                }
                if (!ptr.next.containsKey(dir)) {
                    ptr.next.put(dir, new Trie());
                }
                ptr = ptr.next.get(dir);
            }
            ptr.isEnd = true;
        }
        ArrayList<String> ans = new ArrayList<String>();
        dfs(root, "", ans);
        return ans;
    }
}
           

1234. Replace the Substring for Balanced String

Difficulty:Medium

You are given a string containing only 4 kinds of characters ‘Q’, ‘W’, ‘E’ and ‘R’.

A string is said to be balanced if each of its characters appears n/4 times where n is the length of the string.

Return the minimum length of the substring that can be replaced with any other string of the same length to make the original string s balanced.

Return 0 if the string is already balanced.

Example 1:

Input: s = “QWER”

Output: 0

Explanation: s is already balanced.

Example 2:

Input: s = “QQWE”

Output: 1

Explanation: We need to replace a ‘Q’ to ‘R’, so that “RQWE” (or “QRWE”) is balanced.

Example 3:

Input: s = “QQQW”

Output: 2

Explanation: We can replace the first “QQ” to “ER”.

Example 4:

Input: s = “QQQQ”

Output: 3

Explanation: We can replace the last 3 ‘Q’ to make s = “QWER”.

Constraints:

1 <= s.length <= 10^5

s.length is a multiple of 4

s contains only ‘Q’, ‘W’, ‘E’ and ‘R’.

题意

给定一个只含’Q’,‘W’,‘E’,'R’的字符串,长度为n,求一个最短的连续子串长度,该子串中的字母随意变换,使得变换后的字符串4个字符出现的次数相等,均为n/4

思路

cnt数组记录每个字符出现次数超过n/4应该改成其他字符的个数,即max(times - n/4, 0), subcnt数组记录子串中各个字符出现的次数,问题归结为寻找subcnt[i] >= cnt[i]的子串,i=‘Q’,‘W’,‘E’,‘R’.

用sliding window法求解这个问题。首先window左边界i固定为0, 右边界j不断向右滑动直到第一次找到s[0:j]满足条件,然后左边界i向右滑动直到第一次s[i:j]不满足条件;然后j再向右滑动直到s[i:j]再一次满足条件,i再向右滑动直到s[i:j]再一次不满足条件……直到j滑动到原字符串最右端且s[i:j]已经不满足条件。每次当s[i:j]满足条件时,就用s[i:j]的长度j - i + 1更新结果所要求的最小长度。

由于字符串的每个位置至多被i和j各遍历一次,因此时间复杂度为O(n).

代码

class Solution {
    public int balancedString(String s) {
        int[] cnt = new int[128], subcnt = new int[128];
        for (char ch: s.toCharArray()) {
            ++cnt[ch];
        }
        int i = 0, j = 0, n = s.length(), ans = n - 1, eq = n/4;
        cnt['Q'] = cnt['Q'] > eq? cnt['Q'] - eq: 0;
        cnt['W'] = cnt['W'] > eq? cnt['W'] - eq: 0;
        cnt['E'] = cnt['E'] > eq? cnt['E'] - eq: 0;
        cnt['R'] = cnt['R'] > eq? cnt['R'] - eq: 0;
        if (cnt['Q'] == 0 && cnt['W'] == 0 && cnt['E'] == 0 && cnt['R'] == 0) {
            return 0;
        }
        for (j=0; j<n; ++j) {
            ++subcnt[s.charAt(j)];
            while (j >= i && subcnt['Q'] >= cnt['Q'] && subcnt['W'] >= cnt['W'] && subcnt['E'] >= cnt['E'] && subcnt['R'] >= cnt['R']) {
                ans = Math.min(j - i + 1, ans);
                --subcnt[s.charAt(i++)];
            }
        }
        return ans;
    }
}
           

1235. Maximum Profit in Job Scheduling

Difficulty:Hard

We have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i].

You’re given the startTime , endTime and profit arrays, you need to output the maximum profit you can take such that there are no 2 jobs in the subset with overlapping time range.

If you choose a job that ends at time X you will be able to start another job that starts at time X.

Example 1:

Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]

Output: 120

Explanation: The subset chosen is the first and fourth job.

Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70.

Example 2:

Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]

Output: 150

Explanation: The subset chosen is the first, fourth and fifth job.

Profit obtained 150 = 20 + 70 + 60.

Example 3:

Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]

Output: 6

Constraints:

1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4

1 <= startTime[i] < endTime[i] <= 10^9

1 <= profit[i] <= 10^4

题意

给定n个工作,每个工作有开始时间和结束时间以及报酬,一个人不能同时做两份工作,求可以获得的最大报酬

思路

动态规划。首先将各个工作按结束时间升序排序,dp[i]表示在排序后的第i份的结束时间可以获得的最大报酬。状态转移方程为

方程中的

last

用二分查找O(logn)得到。排序的时间复杂度为O(nlog),动态规划的时间复杂度是O(nlogn),总的时间复杂度是O(nlogn).

代码

class Solution {
    class Range implements Comparable<Range> {
        int start, end, profit;
        
        public Range(int s, int e, int p) {
            start = s;
            end = e;
            profit = p;
        }
        
        @Override
        public int compareTo(Range o) {
            return end - o.end;
        }
    }
    
    private int binarySearch(Range[] ranges, int target) {
        if (target < ranges[0].end) {
            return -1;
        }
        int l = 0, n = ranges.length, r = n - 1, m = 0;
        while (l <= r) {
            m = (l + r) / 2;
            if (ranges[m].end <= target) {
                l = m + 1;
            } else {
                r = m - 1;
            }
        }
        return r;
    }
    
    public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
        int n = profit.length, i = 1, last = -1;
        Range[] ranges = new Range[n];
        for (i=0; i<n; ++i) {
            ranges[i] = new Range(startTime[i], endTime[i], profit[i]);
        }
        Arrays.sort(ranges);
        int[] dp = new int[n];
        dp[0] = ranges[0].profit;
        for (i=1; i<n; ++i) {
            last = binarySearch(ranges, ranges[i].start);
            dp[i] = Math.max((last == -1? 0: dp[last]) + ranges[i].profit, dp[i-1]);
        }
        return dp[n-1];
    }
}