DailyChallenge
392. 判断子序列
Easy
20200727
Description
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:
s = "abc", t = "ahbgdc"
返回 true.
示例 2:
s = "axc", t = "ahbgdc"
返回 false.
后续挑战 :
如果有大量输入的 S,称作S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
链接:https://leetcode-cn.com/problems/is-subsequence
Solution
- 双指针,遍历两个字符串
class Solution {
public boolean isSubsequence(String s, String t) {
int m = s.length();
int n = t.length();
// 双指针遍历,暴力法,但是没有用到s很短,t很长的这一个特点
int i = 0, j = 0;
while(i < m && j < n){
if(s.charAt(i) == t.charAt(j)){
i++;
j++;
}else{
j++;
}
}
return i == m;
}
}
- 后续挑战
动态规划,注意t很长,s很短这个特点。将t字符串预处理。
闫式DP分析法
- 状态表示:dp[i][j]表示字符串t中从i往后,包括i,字符j第一次出现的位置;
dp[n + 1][26]
- 状态计算:找第一次出现的位置,倒序遍历。
if(t.charAt(i) - 'a' == j) dp[i][j] = i;
:如果t的第i个字符就是j,那么第一次出现的位置就是i;else dp[i][j] = dp[i + 1][j];
:如果t的第i个字符不是j,那么就dp[i][j] = dp[i + 1][j]
- 初始化:dp[n][0 ~ 25] = -1 表示找不到。
通过预处理字符串t,如果有大量的s字符串,能够保证O(n)
的时间复杂度。
class Solution {
public boolean isSubsequence(String s, String t) {
// 动态规划预处理t数组
int m = s.length();
int n = t.length();
// 状态表示:dp[i][j]表示字符串t中从i往后,包括i,字符j第一次出现的位置
int[][] dp = new int[n + 1][26];
// 初始化 dp[n][0 ~ 25]
for(int j = 0; j < 26; j++){
// 赋值为-1表示i之后找不到字符j
dp[n][j] = -1;
}
// 状态计算, 倒序动态规划
// 1. if t[i] == j; dp[i][j] = i;
// 2. if t[i] != j; dp[i][j] = dp[i + 1][j];
for(int i = n - 1; i >= 0; i--){
for(int j = 0; j < 26; j++){
if(t.charAt(i) - 'a' == j) dp[i][j] = i;
else dp[i][j] = dp[i + 1][j];
}
}
// 开始找字符串s
int index = 0;
for(int i = 0; i < m; i++){
int c = s.charAt(i) - 'a';
// 如果找不到,就直接返回false
if(dp[index][c] == -1) return false;
// 如果存在,那么c就在t的dp[index][c]位置,所以找下一个字符,应该从dp[index][c] + 1开始找
index = dp[index][c] + 1;
}
return true;
}
}
我的公众号:GitKid
暂时每日分享LeetCode,我在不断学习的过程中,公众号也在不断充实,欢迎大家扫码关注。
TODO:动态规划专题