1. 题目描述
Given a string s and a string t, check if s is subsequence of t. You may assume that there is only lower case English letters in both s and t. t is potentially a very long (length ~= 500,000) string, and s is a short string (<=100). A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, “ace” is a subsequence of “abcde” while “aec” is not).
2. 样例
Example 1:
s = "abc", t = "ahbgdc"
Return true.
Example 2:
s = "axc", t = "ahbgdc"
Return false.
3. 分析
题目的意思是给了两个字符串,需要我们判断第一个字符串是否可以成为第二个字符串的字串:成为条件是第一个字符串按照顺序出现在第二个字符串里面,可以不连续。
拿到问题,最容易想到的思路就是O(n^2)的两层循环:外层为第一个字符串,里层为第二个字符串逐一遍历匹配。可谓是暴力破解,然而我看了一眼题目的设置:第一个字符串长度约为100以内,第二个长度则是500000,暴力破解没得玩,于是另辟蹊径。
3.1. 左右扫描法
于是我想到了模仿快速排序的思路:从左至右开始扫射,两个字符串同时扫射。这样就只需要一层循环遍历即可,循环的条件是第二个字符串t的左边下标小等于右边下标以及外层循环的左右下标不超过边界。当字符串s的左右两边下标相遇并验证完中间的字符,则可以提前退出循环。用图表示如下:
根据这种思想,写成的代码时间复杂度应该是log(n),通过了测试,得到了如下结果:
看来效果还不是特别理想。
3.2. 同向扫描
后来发现根本用不着两边同时扫,从一个方向扫描遍历即可!!时间复杂度是O(n),然后速度比第一种方法要好一些。不过我这里比较奇怪的是理论来说,两边同时扫描应该时间更为节省才对,但是结果的确不如单向扫描的结果。我猜测原因是我写的左右扫描的代码比较冗余,有些重复的计算可以简化,希望有兴趣的朋友在评论区讨论。
4. 源码
4.1. 左右扫描
class Solution {
public:
bool isSubsequence(string s, string t) {
if (s.size() > t.size()) {
return false;
}
int counter_s = 0;
int left_s = 0, left_t = 0, right_s = s.size()-1, right_t = t.size()-1;
while(left_t <= right_t && left_t <= (int)t.size()-1 && right_t >= 0) {
if (s[left_s] == t[left_t]) {
if (left_s == right_s) {
counter_s++;
break;
}
else {
left_s++;
left_t++;
counter_s++;
}
}
else {
left_t++;
}
if (s[right_s] == t[right_t]) {
if (left_s == right_s) {
counter_s++;
break;
}
else {
right_s--;
right_t--;
counter_s++;
}
}
else {
right_t--;
}
}
if (counter_s == (int)s.size()) {
return true;
}
return false;
}
};
4.2. 单向扫描
class Solution {
public:
bool isSubsequence(string s, string t) {
int s_length = s.size(), t_length = t.size();
int index_s = 0;
for (int i = 0; i < t_length && index_s < s_length; i++) {
if(s[index_s] == t[i]) {
index_s++;
}
}
if (index_s == s_length) {
return true;
}
return false;
}
};
5. 心得
算法的思路很重要,然而具体实现的方法也很重要,有些情况下优秀的思路但是复杂的实现不仅不会降低工作强度反而会影响效率。