给定字符串 S 和单词字典 words, 求 words[i] 中是 S 的子序列的单词个数。
示例:
输入:
S = “abcde”
words = [“a”, “bb”, “acd”, “ace”]
输出: 3
解释: 有三个是 S 的子序列的单词: “a”, “acd”, “ace”。
注意:
所有在words和 S 里的单词都只由小写字母组成。
S 的长度在 [1, 50000]。
words 的长度在 [1, 5000]。
words[i]的长度在[1, 50]。
算法分析
由于S的长度可以达到50000,如果对于每个words[k]都搜索一遍字符串S显然会导致算法低效。思路是:对于字符串S的每个字符,用哈希表保存每个字符出现的所有位置,对于words中的某个子串,先在哈希表搜索第一个字符确定子序列的起始位置,之后根据这个字符在字符串S的位置 i 搜索下一个字符在S的位置 j (二分查找),要求有 j > i。需要注意如果某个words子串存在连续重复的字符需要作特殊处理。
代码
class Solution{
public static int numMatchingSubseq(String S, String[] words) {
Map<Character, ArrayList<Integer>> map = new HashMap<>();
int count = 0;
for(int i = 0; i < S.length(); i++) {
char c = S.charAt(i);
if(!map.containsKey(c))
map.put(c, new ArrayList<Integer>(S.length()));
map.get(c).add(i); //保存字符串S每个字符的位置
}
outer:
for(String e: words) {
int preIndex = -1;
for(int i = 0; i < e.length(); i++) {
char c = e.charAt(i);
if(!map.containsKey(c))
continue outer;
ArrayList<Integer> temp = map.get(c);
if(i == 0) { //start of a subsequence
preIndex = temp.get(0);
}else {
int indexOfTemp = Collections.binarySearch(temp, preIndex);
if(indexOfTemp >= 0) {
indexOfTemp++;
if(indexOfTemp >= temp.size())
continue outer;
preIndex = temp.get(indexOfTemp);
} else {
indexOfTemp = -indexOfTemp - 1;
if(indexOfTemp >= temp.size())
continue outer;
preIndex = temp.get(indexOfTemp);
}
}
}
count++;
}
return count;
}
}