LeetCode 424 Longest Repeating Character Replacement
方法:滑动窗口
时间复杂度:O(n)
空间复杂度:O(1)
想法:这题我第一遍做的时候想的方向不对。这题是说选一个子字符串,把其中的一些字母随便换,最多换k次,然后如果整个字符串的字符集只有一个字母的话,求这样的最长子字符串的长度。假设说选择了一个子字符串,显然我们会试图往这个子字符串当中出现最多的那个字符上面换,这样所需要的替换次数是最少的。我一开始想的是用区间的做法,然后枚举每一种字母,看使用k次机会来填掉区间直接的那些值,然后连起来最长的是多少。但是仔细想了想是要超时的。这个题的做法是滑动窗口,想法是对题目做一次转化,就是原题要求的是,对于一个字符串,它的长度减掉里面出现的最多的那个字母出现的次数,小于等于k,满足这样条件的子字符串中长度最长的那个。
具体做法是,在一个for循环里,每次把滑动窗口的右边界往右挪一下。然后在for循环里面,每一次把左边界挪到使窗口满足条件的位置。这样的话每次挪完就会出现一个合法的窗口,窗口长度就是end - start + 1。但这里有个问题要仔细想一下。
在挪左边界的时候,你既然把它挪走了,那相应的,窗口里面这个字母的出现次数就得-1,会更新这个数组。但更新数组的时候,有可能会影响“窗口当中出现次数最多的那个字母出现的次数”,这里用maxc代表。如果考虑这一点,“窗口当中出现次数最多的那个字母出现的次数”实时更新还挺麻烦的。但这题为什么不会每次都更新它呢?因为我们是用maxc来卡窗口的,maxc越大,可能取得的窗口才越大。假设说在某次窗口左边界的移动当中,窗口里面的出现最多的次数事实上是减小了,但是,我们知道maxc是比当前窗口的这个值大的,也就是说我们之前就已经求出来过更大的一个窗口了,所以这个地方更新不更新没意义。就算更新的话也只会让窗口变得更小,根本就不会影响最后的数值,因此这里干脆在移左边界的时候,就不要更新maxc了。
代码:
class Solution {
public int characterReplacement(String s, int k) {
int n = s.length(), start = 0, res = 0, maxc = 0;
int[] map = new int[26];
for (int end = 0; end < n; end++) {
maxc = Math.max(maxc, ++map[s.charAt(end) - 'A']);
while (end - start + 1 - maxc > k) {
map[s.charAt(start) - 'A']--;
start++;
}
res = Math.max(res, end - start + 1);
}
return res;
}
}
LeetCode 417 Pacific Atlantic Water Flow
方法:DFS
时间复杂度:O(mn)
空间复杂度:O(mn)
想法:DFS和BFS都能做。题目本身不难,但主要是是要想清楚搜索顺序。因为左边和上边临太平洋,右边和下边临大西洋,而且高度肯定是>=0,因此边上这一圈肯定是能流进海洋的。所以既然边上这一圈我们知道是可以的,那就可以从边上来开始写递归。这道题能不能做记忆化呢?我觉得是不能的,哪怕对于能不能流入太平洋来说,在某时刻我们遍历到了一个地方,当时遍历的时候认定这个地方不能流入太平洋,但是在后面进行遍历的时候如果再遍历过来,会是从另一条路径遍历过来的,这个点有可能靠着这条路径流入太平洋。因此假如发现这个点已经确定是可以流入太平洋了,那就直接略过,不用再看,否则还是需要再次检查一下。
代码:
class Solution {
private int m, n;
private int[] dx = new int[] {0, -1, 0, 1};
private int[] dy = new int[] {-1, 0, 1, 0};
public List<List<Integer>> pacificAtlantic(int[][] heights) {
m = heights.length;
n = heights[0].length;
boolean[][] canPac = new boolean[m][n];
boolean[][] canAtl = new boolean[m][n];
for (int i = 0; i < m; i++) {
dfs(canPac, i, 0, heights);
dfs(canAtl, i, n - 1, heights);
}
for (int j = 0; j < n; j++) {
dfs(canPac, 0, j, heights);
dfs(canAtl, m - 1, j, heights);
}
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (canPac[i][j] && canAtl[i][j]) {
List<Integer> tmp = new ArrayList<>();
tmp.add(i);
tmp.add(j);
res.add(tmp);
}
}
}
return res;
}
private void dfs(boolean[][] v, int x, int y, int[][] heights) {
v[x][y] = true;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (isValid(nx, ny) && !v[nx][ny] && heights[nx][ny] >= heights[x][y]) {
dfs(v, nx, ny, heights);
}
}
}
private boolean isValid(int x, int y) {
return x >= 0 && x < m && y >= 0 && y < n;
}
}