792. Number of Matching Subsequences




Given string S and a dictionary of words words, find the number of words[i] that is a subsequence of S.

Example :
Input: 
S = "abcde"
words = ["a", "bb", "acd", "ace"]
Output: 3
Explanation: There are three words in words that are a subsequence of S: "a", "acd", "ace".



https://leetcode.com/problems/number-of-matching-subsequences/solution/



Solution 1 : passed 80% cases but TLE on some extremely long strings 
// the total time complexity is the number of words * the length of S + the sum of the words[i].length
// space : 1



class Solution {
    public int numMatchingSubseq(String S, String[] words) {
        int res = 0;
        for(String word : words){ // # of words 
            if(isSubSeq(word, S)) res++;
        }
        return res;
    }
    private boolean isSubSeq(String word, String S){
        int i = 0;
        for(char c : S.toCharArray()){  // the length of S 
            if(i < word.length() && c == word.charAt(i)) i++; // i < word.length()   // for each word, it's words[i].length
        }
        return i == word.length();
    }
}




// use tree set 

class Solution {
    HashMap<Character, TreeSet<Integer>> map;
    public int numMatchingSubseq(String S, String[] words) {
        map = new HashMap<>();
        for(int i = 0; i < S.length(); i++){
            TreeSet<Integer> set = map.get(S.charAt(i));
            if(set == null){
                set = new TreeSet<>();
                map.put(S.charAt(i), set);
            }
            set.add(i);
        }
        
        
        int res = 0;
        for(String word : words){ // # of words 
            if(isSubSeq(word, S)) res++;
        }
        return res;
    }

    private boolean isSubSeq(String s, String t) {
        
        
        int prev = -1;
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            TreeSet<Integer> set = map.get(c);
            if(set == null) return false;
            if(prev == -1){
                prev = set.first();
            }else{
                Integer tmp = set.higher(prev);
                if(tmp == null) return false;
                prev = tmp;
            }
        }
        return true;
        
    }
}



Solution 2 : one pass 

Runtime is linear in the total size of the input (S and all of words).




// time : is linear to  the sum of word[i].length() + s.length()
// space : sum of word[i].length()

class Solution{
    public int numMatchingSubseq(String S, String[] words){
        int res = 0;
        ArrayList<Node>[] array = new ArrayList[26]; // new ArrayList[]
        for(int i = 0; i < 26; i++){
            array[i] = new ArrayList<Node>();
        }
        
        for(String word : words){            // the sum of word.length()
            array[word.charAt(0) - 'a'].add(new Node(word, 0));
        }
        
        for(char c : S.toCharArray()){                   // s.length()
            ArrayList<Node> bucket = array[c - 'a'];
            array[c - 'a'] = new ArrayList<Node>();
            
            for(Node node : bucket){
                node.index++;
                if(node.index == node.word.length()){
                    res++;
                }else{
                    array[node.word.charAt(node.index) - 'a'].add(node);
                }
            }
            bucket.clear();
            // The java.util.ArrayList.clear() method removes all of the elements from this list.The list will be empty after this call returns.
        }
        return res;
    }
}


class Node{
    String word;
    int index;
    public Node(String w, int i){
        word = w;
        index = i;
    }
}






Explanation:

I go through S once, and while I'm doing that, I move through all words accordingly. That is, I keep track of how much of each word I've already seen, and with each letter of S, I advance the words waiting for that letter. To quickly find the words waiting for a certain letter, I store each word (and its progress) in a list of words waiting for that letter. Then for each of the lucky words whose current letter just occurred in S, I update their progress and store them in the list for their next letter.

Let's go through the given example:

S = "abcde"
words = ["a", "bb", "acd", "ace"]

I store that "a", "acd" and "ace" are waiting for an 'a' and "bb" is waiting for a 'b' (using parentheses to show how far I am in each word):

'a':  ["(a)", "(a)cd", "(a)ce"]
'b':  ["(b)b"]

Then I go through S. First I see 'a', so I take the list of words waiting for 'a' and store them as waiting under their next letter:

'b':  ["(b)b"]
'c':  ["a(c)d", "a(c)e"]
None: ["a"]

You see "a" is already waiting for nothing anymore, while "acd" and "ace" are now waiting for 'c'. Next I see 'b' and update accordingly:

'b':  ["b(b)"]
'c':  ["a(c)d", "a(c)e"]
None: ["a"]

Then 'c':

'b':  ["b(b)"]
'd':  ["ac(d)"]
'e':  ["ac(e)"]
None: ["a"]

Then 'd':

'b':  ["b(b)"]
'e':  ["ac(e)"]
None: ["a", "acd"]

Then 'e':

'b':  ["b(b)"]
None: ["a", "acd", "ace"]

And now I just return how many words aren't waiting for anything anymore.
相關文章
相關標籤/搜索