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 inwords
that are a subsequence ofS
: "a", "acd", "ace".
Note:
- All words in
words
andS
will only consists of lowercase letters. - The length of
S
will be in the range of[1, 50000]
. - The length of
words
will be in the range of[1, 5000]
. - The length of
words[i]
will be in the range of[1, 50]
.
=====================================================================
Ideas : There can be no violent solution to this problem.We can use caching,and some details of optimization
=====================================================================
brute force :
public int numMatchingSubseq(String S, String[] words) {
int res=0;
for (String str:words) {
int p = 0;
int size = str.length()-1;
for (int i = 0; p!=size&&i < S.length(); i++) {
if (S.charAt(i)==str.charAt(p)) p++;
}
if (size==p) res++;
}
return res;
}
A bad solution :
public int numMatchingSubseq(String S, String[] words) {
int res = 0;
List<List<Integer>> dict = new ArrayList<>();
for (int i = 0; i < 26; i++) {
dict.add(new ArrayList<>());
}
for (int i = 0; i < S.length(); i++) {
char aim = S.charAt(i);
dict.get(aim-97).add(i);
}
a:for (String str : words) {
for (int i = 0,p = -1; i < str.length(); i++) {
char aim = str.charAt(i);
List<Integer> list = dict.get(aim - 97);
if (list.size()==0||list.get(list.size()-1)<=p) continue a;
for (int j = 0; j < list.size(); j++) {
if (list.get(j)>p){
p=list.get(j);
break ;
}
}
}
res++;
}
return res;
}
Add cache optimization :
public int numMatchingSubseq(String S, String[] words) {
int res = 0;
Map<String, Boolean> map = new HashMap<>();
Node[] nodes = new Node[26];
for (int i = 0; i < S.length(); i++) {
int zmidx = S.charAt(i)-97;
if (nodes[zmidx]==null) nodes[zmidx] = new Node();
nodes[zmidx].ids.add(i);
}
for(int i = 0; i < words.length; i++) {
if (map.containsKey(words[i])) {
if (map.get(words[i]))
res++;
}
else if (check(nodes,S, words[i])) {
res++;
map.put(words[i], true);
} else {
map.put(words[i], false);
}
}
return res;
}
private boolean check(Node[] nodes,String str,String word){
if (str.length()<word.length()) return false;
int p = -1;
for (int i = 0; i < word.length(); i++) {
int index = word.charAt(i)-97;
if (nodes[index]==null||nodes[index].ids.get(nodes[index].ids.size()-1)<=p) return false;
for (int x:nodes[index].ids) {
if (x>p){
p=x;
break;
}
}
}
return true;
}
class Node{
List<Integer> ids = new ArrayList<>();
}
I can't accept that the above method doesn't run very fast , until I saw the test case .
There are too many extreme situations in test cases , causing some abnormal solutions to become fast
such as String.indexOf()
public int numMatchingSubseq(String S, String[] words) {
HashMap<String, Integer> map = new HashMap<>();
int index = 0, count = 0;
boolean sub;
for (String word : words) {
if (map.containsKey(word)) {
count += map.get(word);
} else {
index = -1;
sub = true;
for (int i = 0; i < word.length(); i++) {
index = S.indexOf(word.charAt(i), index + 1);
if (index < 0) {
sub = false;
break;
}
}
if (sub) {
count++;
map.put(word, 1);
} else {
map.put(word, 0);
}
}
}
return count;
}