1024. 满足要求的子串个数
描述
给定一个字符串S 和一个单词字典 words,问, words中一共有多少个单词words[i]是字符串S的子序列?
注意, 子序列不同于子串, 子序列不要求连续.
- 所有words 内的单词和 S只包含小写字母.
- S的长度在 [1, 50000]范围内.
- words的长度在 [1, 5000]范围内.
- words[i]的长度在[1, 50]范围内.
样例 1:
输入:
S = "abcde", words = ["a", "bb", "acd", "ace"]
输出:
3
解释:
words内有三个单词是S的子串: "a", "acd", "ace".
样例 2:
输入:
S = "dsahjpjauf", words = ["ahjpjau","ja","ahbwzgqnuk","tnmlanowax"]
输出:
2
题解
public class Solution {
/**
* @param S: a string
* @param words: a dictionary of words
* @return: the number of words[i] that is a subsequence of S
*/
public int numMatchingSubseq(String S, String[] words) {
// Write your code here
int ret = 0;
int[][] nextPosCache = new int[S.length()][26];
for (String word : words) {
if (this.isSubSeq(S, word, nextPosCache)) {
++ret;
}
}
return ret;
}
/**
* @param S
* @param word
* @param nextPosCache
* @return
*/
private boolean isSubSeq(String S, String word, int[][] nextPosCache) {
int sLen = nextPosCache.length;
int wLen = word.length();
// 比S还长,肯定不是子序列
if (wLen > sLen) {
return false;
}
char[] wcs = word.toCharArray();
int sI, wI;
for (wI = 0, sI = 0; wI < wLen && sI < sLen; ++wI, ++sI) {
char wc = wcs[wI];
int wcI = wc - 'a';
int tmpI = nextPosCache[sI][wcI];
if (tmpI == 0) {
// 缓存未命中
tmpI = S.indexOf(wc, sI);
int maxI;
if (tmpI < 0) {
maxI = wLen - 1;
} else {
maxI = tmpI;
}
for (int i = sI; i <= maxI; ++i) {
if (nextPosCache[i][wcI] != 0) {
break;
}
nextPosCache[i][wcI] = tmpI;
}
}
if (tmpI < 0) {
return false;
}
sI = tmpI;
}
return wI == wLen;
}
}
分析
代码并不复杂,也不难理解:
- numMatchingSubseq方法是入口方法,没什么特别,就是遍历所有待匹配的字符串,匹配成功就将结果自增1。
- isSubSeq方法用来判断参数word是否是参数S的子序列,这是能否通过的关键。而此方法的关键就是S.indexOf(wc, sI)这一句,这是自带的api,就是返回字符串S中从位置sI开始第一个出现wc字符的位置,不存在则返回-1。
- nextPosCache作为延迟加载的缓存,起到加速的作用,可以想象的到,越往后,缓存命中率越高。虽然没有它也完全可以通过,但是有和没有的运行速度有明显的差距。nextPosCache[i][c - ‘a’]的意思就是从位置i开始第一次出现字符c的位置。另外,第一个字符相当于没有缓存,因为判断缓存未命中是用的0,但是结合indexOf的实现,我觉得这里利大于弊,如果缓存未命中的值换成别的,就需要初始化缓存。
最后说两句
非常感谢你阅读本文章,如果你觉得本文对你有所帮助,请留下你的足迹,点个赞,留个言,多谢~
作者水平有限,如果文章内容有不准确的地方,请指正。
希望小伙伴们都能每天进步一点点。
声明
本文由二当家的白帽子博客原创,转载请注明来源,谢谢~