2185. 统计包含给定前缀的字符串
LeetCode: 2185. 统计包含给定前缀的字符串
简单 \color{#00AF9B}{简单} 简单
给你一个字符串数组
words
和一个字符串pref
。返回
words
中以pref
作为 前缀 的字符串的数目。字符串
s
的 前缀 就是s
的任一前导连续字符串。
示例 1:
输入:words = ["pay","attention","practice","attend"], pref = "at"
输出:2
解释:以 "at" 作为前缀的字符串有两个,分别是:"attention" 和 "attend" 。
示例 2:
输入:words = ["leetcode","win","loops","success"], pref = "code"
输出:0
解释:不存在以 "code" 作为前缀的字符串。
提示:
1 <= words.length <= 100
1 <= words[i].length, pref.length <= 100
words[i]
和pref
由小写英文字母组成
方法1:直接遍历
在外层循环中遍历 words
数组;在内部循环中同时遍历words数组中的字符串 word
,以及 pref
。若在 pref
遍历完之前有字符不匹配,即不符合条件,放弃匹配,进行下一次循环。
一点点优化:如果一个字符串
word
以pref
为前缀,那么word
的长度则会大于等于pref
的长度。那么在遍历words
数组时,只要word
的长度小于pref
则直接放弃匹配。
注:若一个字符串
word
与pref
的内容完全一样,则也视作word
以pref
为前缀。
#include <vector>
#include <string>
using namespace std;
class Solution
{
public:
int prefixCount(const vector<string> &words, const string &pref)
{
int count = 0;
for (const string &word : words)
{
if (word.length() < pref.length())
continue;
bool valid = true;
for (auto wit = word.cbegin(), pit = pref.cbegin(); wit != word.cend() && pit != pref.end(); wit++, pit++)
{
if (*wit != *pit)
{
valid = false;
break;
}
}
if (valid)
count++;
}
return count;
}
};
复杂度分析
-
时间复杂度: O ( n × m ) O(n \times m) O(n×m)。其中,
n
为字符串数组words
的长度,m
是字符串pref
的长度。 -
空间复杂度: O ( 1 ) O(1) O(1)。所用变量的占用空间与输入无关,仅占用常数空间。
参考结果
Accepted
95/95 cases passed (8 ms)
Your runtime beats 92.97 % of cpp submissions
Your memory usage beats 96.88 % of cpp submissions (9.5 MB)
方法2:字典树(前缀树)+栈+深度优先搜索
(待更新)
(没必要)
#include <vector>
#include <string>
#include <map>
#include <stack>
using namespace std;
class Solution
{
private:
struct Node
{
map<char, Node *> children;
int count = 0;
};
Node *head;
public:
Solution() : head{new Node()} {}
int prefixCount(const vector<string> &words, const string &pref)
{
Node *p = nullptr;
for (const string &word : words)
{
// 可以剪枝
// if (word.length() < pref.length())
// continue;
p = head;
for (const char &c : word)
{
const auto cit = p->children.find(c);
if (cit != p->children.cend())
p = cit->second;
else
p = p->children.emplace(c, new Node()).first->second;
}
p->count++;
}
p = head;
for (const auto &c : pref)
{
const auto cit = p->children.find(c);
if (cit == p->children.cend())
return 0;
p = cit->second;
}
std::stack<std::pair<
std::map<char, Node *>::const_iterator,
const std::map<char, Node *>::const_iterator>>
stk;
int ans = 0;
if (p->count != 0)
ans += p->count;
stk.emplace(p->children.cbegin(), p->children.cend());
while (!stk.empty())
{
auto &[cit, cend] = stk.top();
if (cit == cend)
{
stk.pop();
continue;
}
if (cit->second->count != 0)
ans += cit->second->count;
if (cit->second->children.size() != 0)
stk.emplace(cit->second->children.cbegin(), cit->second->children.cend());
cit++;
}
return ans;
}
~Solution() {}
};
std::map在本例中,在时间和空间的表现上比std::unordered_map更好。
复杂度分析
-
时间复杂度: O ( n × ( m + L ‾ ) ) O(n \times ( m + \overline{L} )) O(n×(m+L))。其中,
n
为字符串数组words
的长度,m
是字符串pref
的长度, L ‾ \overline{L} L是字符串数组words
中的字符串平均长度。- 构建字典树需按方法1中那样遍历
words
和pref
,需要 O ( n × m ) O(n \times m) O(n×m)的时间复杂度。 - 遍历字典树,定位深度搜索的起点时,需要 O ( m ) O(m) O(m)的时间复杂度。
- 进行深度优先搜索统计单词个数时,需遍历每个记录在字典树中的字符结点。将
words
中所有字符串的总长除以words
数组的长度n
,得到字符串均长 L ‾ \overline{L} L。所需时间复杂度为 O ( n × L ‾ ) O(n \times \overline{L}) O(n×L)。 - 总的来说,时间复杂度为 O ( n × m + m + n × L ‾ ) = O ( n × m + n × L ‾ ) = O ( n × ( m + L ‾ ) ) O(n \times m + m + n \times \overline{L}) = O(n \times m + n \times \overline{L}) = O(n \times ( m + \overline{L} )) O(n×m+m+n×L)=O(n×m+n×L)=O(n×(m+L))。
- 构建字典树需按方法1中那样遍历
-
空间复杂度: O ( n × L ‾ ) O(n \times \overline{L}) O(n×L)。字典树需要 O ( n × L ‾ ) O(n \times \overline{L}) O(n×L)的空间;栈需要 O ( L ‾ ) O(\overline{L}) O(L)的空间。两者作加法。
参考结果
95 / 95 个通过测试用例
执行用时: 44 ms
内存消耗: 30 MB