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).
Example 1:
s = “abc”, t = “ahbgdc”
Return true.
Example 2:
s = “axc”, t = “ahbgdc”
Return false.
没有明确给出。
我的思路很直观,设置两个指针i,j分别用于扫描字符串s和字符串t。
- 对于每一个s[i],都判断当前的t[j]是否等于s[i],一旦不相等就返回false,相等就增加i判断s的下一个字符。
- 若i扫描了s的所有字符则返回true。
- 由于题目说了字符串是要保持相对顺序的,所以j只要一直往后扫描t即可。最后j到达t的尾部时,返回false,因为这时表明没有在t中找到s的所有字符。
有大牛对我那种思路的算法做了优化,优化的地方是调用STL中string的find_first_of()函数找到t中第一个与s[i]匹配的字符的位置,判断是否为有效位置,是的话继续用此方式找下一个匹配s[i]的位置,直到找不到匹配位置(返回false)或是找到了所有s[i]的匹配位置(返回true)。
见参考代码1。
另外一种思路是对t进行了预处理,建立英文字母的动态数组,使用的是vector<vector<int>>
其中A对应第一个数组,B对应第二个数组。对于t[i],都把i放入到以(t[i]-‘A’)为索引的数组中。
然后对于每一个s[j],都在相应的以(s[j]-‘A’)为索引的数组中寻找并记录t字符串的下标i值,如果没有这样i,或是所有找到的i比上一个s[j]对应的i小,则返回false,最后返回true。
见参考代码2。
class Solution {
public:
bool isSubsequence(string s, string t) {
if (s == "")
return true;
for (int i = 0,j = 0; j < t.length(); j++) {
if (t[j] == s[i]) {
i++;
if (i == s.length())
return true;
}
}
return false;
}
};
class Solution {
public:
bool isSubsequence(string s, string t) {
int indexS = 0;
int indexT = 0;
for (; indexS < s.size(); indexS++) {
if (indexT > t.size() - 1)
return false;
size_t lastIndex = t.find_first_of(s[indexS], indexT);
if (lastIndex != string::npos)
indexT = ++lastIndex;
else
return false;
}
return true;
}
};
class Solution {
public:
bool isSubsequence(string s, string t) {
vector<vector<int>> char_pos(26);
for(int i=0; i<t.size(); ++i)
char_pos[t[i]-'a'].push_back(i);
// remember the previous index of t
int t_idx = -1;
for(char c:s){
int j = c - 'a';
bool okay = false;
for(int idx:char_pos[j]){
if(idx > t_idx){
// found an available character in t
okay = true;
t_idx = idx;
break;
}
}
if(okay==false) return false;
}
return true;
}
};
题目还提出了更进一步的问题:
If there are lots of incoming S, say S1, S2, … , Sk where k >= 1B, and you want to check one by one to see if T has its subsequence. In this scenario, how would you change your code?
这个问题是说如果有许多的待判断字符串s需要我们去跟字符串t比对,判断这些s是否为t的子字符串。
这里有两种思路。一种就是逐个处理字符串s,这样的话,可以重用之前的代码,对于每一个s都跑一遍上面的代码,相较而言,选择参考代码2的算法会高效一些,只要全局存储了对t预处理后的动态数组,就可以很快地判断出s是否为t的子字符串,而其他代码都得遍历一遍t,效率要低得多。
然而第一种思路是一种串行的处理,更快地当然是并行处理所有字符串s,只要有一个字符串满足了就可以直接返回true。这种思路的过程如下:
1.使用vector保存所有s的迭代器,暂称为iters
2.使用vector<vector<int>>
在s当前字符对应的数组中保存s的编号,该数据结构暂称为waitingList,大小是26个1维动态数组,比如s1当前待比较的字符是g,那么在waitingList[‘g’ - ‘a’]中存储编号1。初始时,各个s的编号保存在s第一个字符对应的数组,可能存在某个数组有多编号(所以使用vector来保存)。
3.使用一个整型变量unfinished保存已被处理完的s的数目。
3.从t的第一位开始扫描,把waiting[ t[i] - ‘a’]数组清空,并对在这数组里保存的编号所对应的字符串s都进行更新,更新操作为利用iters把s的编号保存到下一个s字符对应的数组中。比如t[0] = ‘a’,waitingList[0] = {2, 8},而s2 = “ab”, s8 = “ad”,那么进行的操作是清空waitingList[0],并且把编号2保存到waitingList[1]的末尾,把编号8保存到waitingList[7]的末尾。
4.如果某个s已经被遍历完,则可以让unfinished自减1。
5.重复3-4直到unfinished为0或是遍历完了t。判断iters中的各个s的迭代器是否为尾后迭代器。是的话记录true,否的话记录false,把结果集返回即可。
这个解法的来源见LeetCode的discuss
vector<bool> isSubsequence(vector<string> ss, string t) {
vector<string::const_iterator> iters(ss.size());
vector<vector<int> > waitingList(26);
int unfinished = ss.size();
for(int i = 0; i < ss.size(); i++) {
iters[i] = ss[i].begin();
if(iters[i] != ss[i].end()) waitingList[*iters[i] - 'a'].push_back(i);
}
for(char c : t) {
vector<int> updateList = waitingList[c - 'a'];
waitingList[c - 'a'].clear();
for(int i : updateList) {
iters[i]++;
if(iters[i] != ss[i].end()) waitingList[*iters[i] - 'a'].push_back(i);
else unfinished--;
}
if(unfinished == 0) break;
}
vector<bool> ans(ss.size());
for(int i = 0; i < ss.size(); i++) {
ans[i] = iters[i] == ss[i].end();
}
return ans;
}
这道题虽然是水题,但是它的深入思考题确实很有启发性,也有一定的难度,从大牛的并行的解法中,我获益匪浅,并且还知道了一些未曾见过的函数,比如find_first_of(),真是开心。
国庆第一天就学到这么多东西,太棒了,顺便说一句祖国母亲生日快乐!国庆假期我们一起加油吧↖(^ω^)↗。