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份的结束时间可以获得的最大报酬。状态转移方程为
dp[i] = max(dp[i-1], dp[last] + profit[i]), endTime[last] <= startTime[i] && endTime[last+1] > startTime[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];
}
}