LeetCode 424, 417

255 阅读4分钟

LeetCode 424 Longest Repeating Character Replacement

链接:leetcode.com/problems/lo…

方法:滑动窗口

时间复杂度: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

链接:leetcode.com/problems/pa…

方法: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;
    }
}