392. 判断子序列

这里写图片描述


1、动归

首先想到的就是利用动归,先从简单的开始,利用一个二维辅助数组 dp[i][j],表示 s.sub(0,i) 是否为 t.sub(0,j)的子序列,其中有转换方程:

if (dp[i][j - 1]) dp[i][j] = true;
else dp[i][j] = dp[i - 1][j - 1] && (s.charAt(i) == t.charAt(j));

即如果 s.sub(0,i)t.sub(0,j-1)的子序列,则肯定也是 t.sub(0,j)的子序列;否则,就需要看 s.sub(0,i-1) 是否为 t.sub(0,j-1)的子序列,如果是的话就只要比较 s(i)t(j)是否相等,相等则 s.sub(0,i)t.sub(0,j)的子序列。

public static boolean isSubsequence(String s, String t) {
    if (s == null || s.length() == 0) return true;
    //该判断需要放在上一判断的后面,不然就要额外判断 t.length()==0 且 s.length()==0 的情况,此时应该是返回 true 的
    if (t == null || t.length() == 0 || s.length() > t.length()) return false;
    int len1 = s.length(), len2 = t.length();
    boolean[][] dp = new boolean[len1][len2];
    dp[0][0] = s.charAt(0) == t.charAt(0);
    //第一排的需提前确定好,因为下面有涉及 dp[i-1]
    for (int j = 1; j < len2; j++) {
        //可能存在 s.chatAt(0) 与 t.chatAt(0) 不相等但是与 t.chatAt(j>0) 相等的情况
        dp[0][j] = dp[0][j - 1] || s.charAt(0) == t.charAt(j);//
    }
    for (int i = 1; i < len1; i++) {
        dp[i][i] = dp[i - 1][i - 1] && (s.charAt(i) == t.charAt(i));
    }
    for (int i = 1; i < len1; i++) {
        for (int j = i + 1; j < len2; j++) {
            if (dp[i][j - 1]) dp[i][j] = true;
            else dp[i][j] = dp[i - 1][j - 1] && (s.charAt(i) == t.charAt(j));
            //如果已经完整的判断了 s 字符串是 t 的子串,就没必要判断后面的了
            if (i == len1 - 1 && dp[i][j]) return true;
        }
    }
    return dp[len1 - 1][len2 - 1];
}

2、基于单次遍历

参考自 用时为 2 ms 的范例

该实现只需要 O(t.length())的时间复杂度,即只需要遍历 t.length() 的长度

public static boolean isSubsequence2(String s, String t) {
    if (s == null || s.length() == 0) return true;
    //该判断需要放在上一判断的后面,不然就要额外判断 t.length()==0s.length()==0 的情况,此时应该是返回 true 的
    if (t == null || t.length() == 0 || s.length() > t.length()) return false;

    // index 用于遍历 s 的每个字符,看每一个字符是否按相对位置存在于 t 中
    // 如果 s(i) 在 t(j) 的位置,则 s(i+1) 只需要从 t(j+1) 遍历比对
    for(int index = 0,pos = 0; index < s.length(); index++,pos++){
        pos = t.indexOf(s.charAt(index),pos);
        if(pos == -1){
            return false;
        }
    }
    return true;
}

for(int index = 0,pos = 0; index < s.length(); index++,pos++){
    pos = t.indexOf(s.charAt(index),pos);
    if(pos == -1){
        return false;
    }
}

扩展即为:

int tPos = 0;
for (int i = 0; i < s.length(); i++) {
    int j = tPos;
    tPos=-1;
    for (; j < t.length(); j++) {
        if (s.charAt(i) == t.charAt(j)) {
            tPos = j + 1;
            break;
        }
    }
    if (tPos == -1) return false;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值