LeetCode 392. Is Subsequence 解题报告

LeetCode 392. Is Subsequence 解题报告

题目描述

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的所有字符。

参考思路1:

有大牛对我那种思路的算法做了优化,优化的地方是调用STL中string的find_first_of()函数找到t中第一个与s[i]匹配的字符的位置,判断是否为有效位置,是的话继续用此方式找下一个匹配s[i]的位置,直到找不到匹配位置(返回false)或是找到了所有s[i]的匹配位置(返回true)。
见参考代码1。

参考思路2:

另外一种思路是对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;
    }
};

参考代码1

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;
    }
};

参考代码2

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(),真是开心。
国庆第一天就学到这么多东西,太棒了,顺便说一句祖国母亲生日快乐!国庆假期我们一起加油吧↖(^ω^)↗。

你可能感兴趣的:(LeetCode)