1、字典树
我们可以对字符串 s s s进行字典树的建模,而后我们在查询 w o r d s words words时只需要在字典树结构内进行查询即可。
class Solution {
struct trie {
array<std::unique_ptr<trie>, 26> arr;
int cnt = 0;
void build(std::string::iterator begin, std::string::iterator end) {
if (begin == end) {
++cnt;
return;
}
int x = *begin - 'a';
if (!arr[x]) {
arr[x] = std::make_unique<trie>();
}
arr[x]->build(++begin, end);
}
void merge(std::unique_ptr<trie> node) {
if (node == nullptr) {
return;
}
cnt += node->cnt;
for (int i = 0; i < 26; ++i) {
if (node->arr[i]) {
if (!arr[i]) {
arr[i] = std::move(node->arr[i]);
} else {
arr[i] -> merge(std::move(node->arr[i]));
}
}
}
}
};
public:
int numMatchingSubseq(string s, vector<string>& words) {
trie root;
for (auto& word : words) {
root.build(word.begin(), word.end());
}
for (char c: s) {
root.merge(std::move(root.arr[c-'a']));
}
return root.cnt;
}
};
2、二分查找
我们可以利用一个二维数组来记录字符串 s s s中每一个字符对应的位置,并将其进行升序排列。当我们在比较两个字符串是否为匹配子序列时,我们对于 w o r d s words words中的第 j j j位字符查询其在二维数组中对应的数组,我们只需要证明在每一次查找中查找到的值都大于上一次查找的位置即可确定两个字符串匹配子序列。我们在这里可以使用二分搜索来代替遍历进行优化。
class Solution {
public:
int numMatchingSubseq(string s, vector<string> &words) {
vector<vector<int>> pos(26);
for (int i = 0; i < s.size(); ++i) {
pos[s[i] - 'a'].push_back(i);
}
int res = words.size();
for (auto &w : words) {
if (w.size() > s.size()) {
--res;
continue;
}
int p = -1;
for (char c : w) {
auto &ps = pos[c - 'a'];
auto it = upper_bound(ps.begin(), ps.end(), p);
if (it == ps.end()) {
--res;
break;
}
p = *it;
}
}
return res;
}
};
3、多指针
我们可以使用指针让字符串 s s s一对多的进行匹配从而提升搜索速度。
class Solution {
public:
int numMatchingSubseq(string s, vector<string> &words) {
vector<queue<pair<int, int>>> queues(26);
for (int i = 0; i < words.size(); ++i) {
queues[words[i][0] - 'a'].emplace(i, 0);
}
int res = 0;
for (char c : s) {
auto &q = queues[c - 'a'];
int size = q.size();
while (size--) {
auto [i, j] = q.front();
q.pop();
++j;
if (j == words[i].size()) {
++res;
} else {
queues[words[i][j] - 'a'].emplace(i, j);
}
}
}
return res;
}
};