题目描述
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
后续挑战:如果有大量输入的 S,称作S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
解题思路
子序列不要求连续,仅仅要求不改变字符的相对顺序即可。
- 直接法:直接判断子序列这个没什么好说的了,直接
就可以得出结果,不需要动态规划,反而麻烦了。
- 分别给两个字符串设置两个指针(
index
),在t
中去匹配s
,s
能完全匹配时返回true
,一次遍历即可。
- 分别给两个字符串设置两个指针(
- 动态规划法:纯粹是为了练习下“动态规划”才这么做的,时间/空间相率不高。
- 状态:
dp[i][j]
为s
的从头开始到i
的子字符串是否为t
从头开始到j
的子字符串的子序列。 - 状态转移公式:
- 当
char[i] == char[j]
时,dp[i][j] = dp[i-1][j-1]
; - 当
char[i] != char[j]
时,即判断当前0~i
子字符串是否是0~j-1
的子字符串的子序列,即dp[i][j] = dp[i][j-1]
。
- 当
- 初始化:空字符串一定是
t
的子字符串的子序列,所以dp[0][j] = true
,其余位置均初始化为false
。(由状态转移方程可知,其实这里需要同时初始化第0
行和第0
列,但是可以先指定二维数组全为false
,然后只初始化第0
行就可以了)
- 状态:
- 后续挑战:重要的是要明白思路,肯定是用空间换时间。
- 创建一个哈希表,存储
t
中26个小写字母及每个字母出现的下标,下标按照升序排序。(也可用数组实现简单的哈希表:vector<vector<int>>dp(26)
) - 例如:
dp[0]
存储了字符a
出现在t
的所有位置的下标,逐个字符判断s
的字符顺序是否在t
内,直接返回结果。 - 因为索引是递增的,所以我们可以使用二分查找
s
的单个字符。(见题解代码即可)
- 创建一个哈希表,存储
参考代码
class Solution {
public:
bool isSubsequence(string s, string t) {
if(s.size() == 0 && t.size() == 0)
return true;
int indexOfS = 0;
for(int i = 0; i < t.size(); i++){
if(t[i] == s[indexOfS])
indexOfS++;
if(indexOfS == s.size())
return true;
}
return false;
}
};
后续挑战(题解代码)
bool isSubsequence(string s, string t) {
vector<vector<int> > dp(26);
int tag=-1;
for(int i=0;i<t.size();i++)
dp[t[i]-'a'].push_back(i);
for(int i=0;i<s.size();i++){
int now=s[i]-'a';
int left=0,right=dp[now].size()-1;
while(left<right){
int mid=(left+right)/2;
if(dp[now][mid]>tag)
right=mid;
else
left=mid+1;
}
if(right<left || dp[now][left]<tag)return false;
tag=dp[now][left];
}
return true;
}