🌕🌕 392. 判断子序列

吞佛童子2022年6月9日
  • algorithm
  • 双指针
  • dp
  • 大数据
大约 3 分钟

🌕🌕 392. 判断子序列

难度: 🌕🌕

问题描述

img_19.png


解法 1 - 双指针

class Solution {
    public boolean isSubsequence(String s, String t) {
        // 思路:
        // 双指针
        int rows = s.length();
        int cols = t.length();
        int m = 0; 
        int n = 0;
        while(m < rows && n < cols) {
            if(s.charAt(m) == t.charAt(n)) {
                m ++;
                n ++;
            } else {
                n ++;
            }
        }
        return m == rows;
    }
}

输出 1

img_21.png


解法 2 - dp

class Solution {
    public boolean isSubsequence(String s, String t) {
        // 思路:
        // dp[i][j] = dp[i][j - 1], dp[i - 1][j - 1]
        int rows = s.length();
        int cols = t.length();
        boolean[][] dp = new boolean[rows + 1][cols + 1];
        // 初始化
        for(int j = 0; j <= cols; j ++) {
            dp[0][j] = true;
        }
        // dp
        for(int i = 1; i <= rows; i ++) {
            for(int j = 1; j <= cols; j ++) {
                int m = i - 1;
                int n = j - 1;
                if(s.charAt(m) == t.charAt(n)) {
                    dp[i][j] |= dp[i - 1][j - 1];
                }
                dp[i][j] |= dp[i][j - 1];
            }
        }
        return dp[rows][cols];
    }
}

输出 2

img_20.png


解法 3 - 进阶问题

class Solution {
    public boolean isSubsequence(String s, String t) {
        // 思路:
        // 进阶问题
        // 常规情况下,我们需要同时遍历 s & t 直到一方截止,最差情况下遍历 t 的长度
        // 如果 t 不变,s 需要经常和 t 匹配,那么我们可以预先对 t 进行一些前置处理,使得判断时可以跳跃着匹配,只需遍历 s 的长度
        // 那么如何做?
        // 假设遍历 s 的 cur 时对应 t 的下标为 index,我们想要快速得到 s 下一个字符 next 对应 t 所在下标
        // 因此需要存储 t 的任意下标处到任意字符的下标
        // 例 s = bac, t = ababc
        // s 想要 b,我们需要快速得到 t 中第一个 b 所在下标, 为 1
        // s 想要下一个字符 a,我们在 t 当前下标 1 处快速得到从这里出发,后续的第一个 a 所在下标,即 2
        // s 想要下一个字符 c,我们在 t 下标 2 处快速得到从 2 以后的第一个 c 所在下标,即 4
        // s 遍历完成,满足条件
        int m = s.length();
        // 对 t 进行预处理
        t = " " + t; // 由于 s 的首个字符并不一定对应 t 的首个字符,我们需要找到 s 首字符对应 t 的首字符位置
        int n = t.length();
        // 因此需要添加一个入口字符,这个入口字符,含有到 s 的 26 种任意起始字符的路径下标
        int[][] arr = new int[n][26];
        // 如果外层遍历 t 的下标,复杂度为 O(N),内层找 [a - z] 所有可能情况字符的下标,仍需要遍历一遍 t,复杂度为 O(N)
        // 若外层遍历 [a - 1] 所有可能情况的字符,复杂度为 O(26),内层遍历 t 的下标,找该下标处下一个字符的下标,复杂度为 O(N)
        // 所以采用 外层遍历 字符,内层遍历 t 的方式填充 arr[]
        for(char c = 'a'; c <= 'z'; c ++) {
            // 当前要寻找的下标为 c
            int y = c - 'a'; // 对应 arr 的纵坐标
            int val = -1; // 如果从 t 的某个下标之后不会出现 c,则 val == -1 表示没有
            // 从后往前遍历
            for(int i = n - 1; i >= 0; i --) {
                char cur = t.charAt(i);
                arr[i][y] = val; 
                if(cur == c) {
                    // 当前下标处的字符与想要寻找的字符匹配,那么再往前遇到该字符,下标更新为当前下标
                    val = i;
                }
            }
        }
        // 遍历 s
        int index = 0; // t 的初始下标,对应 ' ',里面存放了 26 种字符的入口下标
        for(int i = 0; i < m; i ++) {
            char c = s.charAt(i); // 想要查找的字符
            // 到 t 中查找,初始时从 下标 0 的 ' ' 入口找到初始下标
            index = arr[index][c - 'a'];
            if(index == -1) {
                return false; // 说明 t 从当前下标查找,往后不存在 s 想要的字符 c
            }
        }
        return true;
    }
}

输出 3

img_13.png

上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou