LeetCode 763
方法1:区间合并(不推荐)
时间复杂度:O(nlogn) 想法:我第一遍写的时候想法就是找每个字母出现的范围,就是第一次出现和最后一次出现的下标,然后就会得到很多区间,然后就可以对有交集的区间进行合并,最后算有多少个区间。 代码:
class Solution {
public List<Integer> partitionLabels(String s) {
int[][] map = new int[26][2];
for (int i = 0, j = s.length() - 1; i < s.length(); i++, j--) {
map[s.charAt(i) - 'a'][1] = i + 1;
map[s.charAt(j) - 'a'][0] = j + 1;
}
List<int[]> intervals = new ArrayList<>();
for (int[] pair : map) {
if (pair[0] == 0 && pair[1] == 0) {
continue;
}
intervals.add(pair);
}
Collections.sort(intervals, (a, b) -> a[0] - b[0]);
int start = intervals.get(0)[0];
int end = intervals.get(0)[1];
List<Integer> res = new ArrayList<>();
for (int[] interval : intervals) {
if (interval[0] <= end) {
end = Math.max(end, interval[1]);
}
else {
res.add(end - start + 1);
start = interval[0];
end = interval[1];
}
}
res.add(end - start + 1);
return res;
}
}
方法2:哈希
时间复杂度:O(n) 想法:先扫一遍数组记录一下每个字母最后出现的地方。维护一个变量rightMost,指我假设说要包含这些前面的字母,那么这个区间至少要在后面囊括到哪里。然后这么扫过去,每次遇到一个字母,就更新一下rightMost,要保证rightMost是最大的。然后如果有一次扫到一个字母,i == rightMost了,那说明卡到rightMost这里可以形成一个合法的区间了。那么这里会发现得记录上一次这样i == rightMost是在哪,然后这一段区间的长度就可以求出来了,然后放进结果列表。 代码:
class Solution {
public List<Integer> partitionLabels(String s) {
int[] last = new int[26];
int n = s.length();
for (int i = 0; i < n; i++) {
last[s.charAt(i) - 'a'] = i;
}
List<Integer> res = new ArrayList<>();
int prev = 0, rightMost = 0;
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
rightMost = Math.max(rightMost, last[c - 'a']);
if (i == rightMost) {
res.add(rightMost - prev + 1);
prev = i + 1;
}
}
return res;
}
}
LeetCode 1237
方法:跟search matrix II 一样,就找规律扫描
时间复杂度:O(m + n) 想法:这个题如果是在以前的话可能还能写一写分析,但是在2021年大家也都知道这个题怎么做了。既然这个矩阵顺着行增大方向单调递增,顺着列增大方向也单调递增,那么应该考虑副对角线。比方说现在在矩阵的左下角,我们知道它上面的跟它一列的全都比它小,它右面的跟它一行的全都比它大,那么我们就直接看这个元素跟要找的target值比较怎么样,如果比target大,就行减一,因为这一行反正不会找到了;如果比target小,就列加一,因为它左边反正没有一个有戏的。 代码:
/*
* // This is the custom function interface.
* // You should not implement it, or speculate about its implementation
* class CustomFunction {
* // Returns f(x, y) for any given positive integers x and y.
* // Note that f(x, y) is increasing with respect to both x and y.
* // i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
* public int f(int x, int y);
* };
*/
class Solution {
public List<List<Integer>> findSolution(CustomFunction customfunction, int z) {
List<List<Integer>> res = new ArrayList<>();
int left = 1, right = 1000;
while (left <= 1000 && right >= 1) {
int tmp = customfunction.f(left, right);
if (tmp < z) {
left++;
}
else if (tmp > z) {
right--;
}
else {
List<Integer> lst = new ArrayList<>();
lst.add(left);
lst.add(right);
left++;
right--;
res.add(lst);
}
}
return res;
}
}
LeetCode 240
方法1:同上,找规律扫描
时间复杂度:O(m + n) 想法:同上。 代码:
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int x = m - 1, y = 0;
while (x >= 0 && y < n) {
if (matrix[x][y] == target) {
return true;
}
else if (matrix[x][y] > target)
while (x >= 0 && matrix[x][y] > target) x--;
else
while (y < n && matrix[x][y] < target) y++;
}
return false;
}
}
方法2:二分
时间复杂度:O(min(m, n) * (logn+logm)) 想法:二分up,down,left,right,代表这个target可能出现的区域。在up里二分right,down里二分left,然后继续在left里分down,right里分up。想法是这样,比方说在up这一行里面,up是指行数比较小的那一行,那么如果这一行的某个值大于target,那它右边和下边就不用看了,肯定大于target,那么right直接移到这里之前。这个做法我是觉得写起来相似的代码太多了,而且写起来比较长,时间复杂度也不是很好,我不是很喜欢这种做法,有空的话写写,不然就不写了。 代码:参考www.acwing.com/solution/co…