392. 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
思路
双指针
双指针的思路很容易想到,用两个指针分别指向s和t,字符相同时都前进一步,否则t字符串的指针前进,如果到结束s的指针还没移动到结尾,证明无法匹配。
代码
class Solution {
public:
bool isSubsequence(string s, string t) {
int s_index = 0, t_index = 0;
while(s_index < s.size() && t_index < t.size()) {
if(s[s_index] == t[t_index]) {
s_index++;
}
t_index++;
}
return s_index == s.size();
}
};
动态规划
思考题假定了s的数量很大,如果每次都使用双指针方法,时间都消耗在t上寻找匹配字符了,需要对t字符串进行预处理操作,令dp[i][j]表示t字符串中从位置i开始往后第一次出现字符j的位置。
当t[i]就是字符j时,dp[i][j]=i,否则字符j出现的位置在i+1往后,故dp[i][j]=dp[i+1][j]。
边界条件,dp[m][j]=m,表示从位置m往后不可能出现字符j。
代码
class Solution {
public:
bool isSubsequence(string s, string t) {
int n = s.size(), m = t.size();
vector<vector<int>> dp(m + 1, vector<int>(26, 0));
for(int i = 0; i < 26; i++) {
dp[m][i] = m;
}
for(int i = m - 1; i >= 0; i--) {
for(int j = 0; j < 26; j++) {
if(j == t[i] - 'a') {
dp[i][j] = i;
}
else {
dp[i][j] = dp[i + 1][j];
}
}
}
int odd = 0;
for(int i = 0; i < n; i++) {
if(dp[odd][s[i] - 'a'] == m) {
return false;
}
odd = dp[odd][s[i] - 'a'] + 1;
}
return true;
}
};