412,判断子序列_子序列

Tough time don't last, tough people do.

没有过不去的坎, 只有打不倒的人。

问题描述

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。


示例 1:

s = "abc", t = "ahbgdc"

返回 true.


示例 2:

s = "axc", t = "ahbgdc"

返回 false.


双指针求解

这题让求的是s是否是t的子序列,我们可以使用两个指针,一个指向s的某个字符,一个指向t的某个字符,其中指向t的指针每次都会往右移一位,指向s的指针每次和指向t的指针所对应的字符相同时才会往右移,否则就不移动,当指向s的指针指向s的末尾的时候,返回true。这里以示例1为例来画个图看一下

412,判断子序列_子序列_02412,判断子序列_子序列_03412,判断子序列_子序列_04

原理很简单,我们来直接看下代码

1public boolean isSubsequence(String s, String t) { 2    if (s.length() == 0) 3        return true; 4    int indexS = 0, indexT = 0; 5    while (indexT < t.length()) { 6        if (t.charAt(indexT) == s.charAt(indexS)) { 7            //指向s的指针只有在两个字符串相同时才会往右移 8            indexS++; 9            if (indexS == s.length())10                return true;11        }12        //指向t的指针每次都会往右移一位13        indexT++;14    }15    return false;16}


动态规划

我们用dp[i][j]表示字符串t的前j个字符包含s的前i个字符

所以递归公式是


1,s.charAt(i - 1) == t.charAt(j - 1)

dp[i][j] = dp[i - 1][j - 1]


2,s.charAt(i - 1) != t.charAt(j - 1)

dp[i][j] = dp[i][j - 1];


那么边界条件是什么呢,当s为空的时候,我们默认t是包含s的,所以当s为空的时候,返回true。有了递推公式和边界条件,代码就很容易写了,来看下

1public boolean isSubsequence(String s, String t) { 2    if (s.length() == 0) 3        return true; 4    boolean[][] dp = new boolean[s.length() + 1][t.length() + 1]; 5    //边界条件 6    for (int i = 0; i < t.length(); i++) { 7        dp[0][i] = true; 8    } 9    for (int i = 1; i <= s.length(); i++) {10        for (int j = 1; j <= t.length(); j++) {11            //递推公式12            if (s.charAt(i - 1) == t.charAt(j - 1)) {13                dp[i][j] = dp[i - 1][j - 1];14            } else {15                dp[i][j] = dp[i][j - 1];16            }17        }18    }19    return dp[s.length()][t.length()];20}21


逐个查找

如果熟悉java语言的都知道,在java中String类有这样一个方法

public int indexOf(int ch, int fromIndex)

他表示的是在字符串中是否存在一个字符ch,并且是从字符串的下标fromIndex开始查找的。我们要做的是在t字符串中查找s中的每一个字符,如果没查到,直接返回false。如果查到,就从t的下一个位置继续开始查

1public boolean isSubsequence(String s, String t) { 2    int index = -1; 3    for (char c : s.toCharArray()) { 4        //index表示上一次查找的位置(第一次查找的时候为-1), 5        // 所以这里要从t的下标(index+1)开始查找 6        index = t.indexOf(c, index + 1); 7        //没找到,返回false 8        if (index == -1) 9            return false;10    }11    return true;12}


参照最长公共子序列

看到这道题我们还可以参照之前写的370,最长公共子串和子序列,我们只要求出s和t的最长公共子序列的长度即可,如果长度等于s的长度,说明s就是t的子序列

1public boolean isSubsequence(String s, String t) { 2    int[] dp = new int[t.length() + 1]; 3    int last = 0; 4    for (int i = 1; i <= s.length(); i++) { 5        for (int j = 1; j <= t.length(); j++) { 6            int temp = dp[j]; 7            if (s.charAt(i - 1) == t.charAt(j - 1)) 8                dp[j] = last + 1; 9            else10                dp[j] = Math.max(dp[j], dp[j - 1]);11            last = temp;12        }13    }14    return dp[t.length()] == s.length();15}


总结

这题比较简单,但解法比较多,最容易想到的估计就是双指针了。




412,判断子序列_子序列_05

长按上图,识别图中二维码之后即可关注。


如果喜欢这篇文章就点个"赞"吧