题目描述
有时候人们会用重复写一些字母来表示额外的感受,比如 "hello" -> "heeellooo"
,"hi" -> "hiii"
。我们将相邻字母都相同的一串字符定义为相同字母组,例如:"h"
,"eee"
,"ll"
,"ooo"
。
对于一个给定的字符串 S
,如果另一个单词能够通过将一些字母组扩张从而使其和 S
相同,我们将这个单词定义为可扩张的(stretchy)。扩张操作定义如下:选择一个字母组(包含字母 c
),然后往其中添加相同的字母 c
使其长度达到 3
或以上。
以 "hello"
为例,我们可以对字母组 "o"
扩张得到 "hellooo"
,但是无法以同样的方法得到 "helloo"
因为字母组 "oo"
长度小于 3
。此外,我们可以进行另一种扩张 "ll" -> "lllll"
以获得 "helllllooo"
。如果 S = "helllllooo"
,那么查询词 "hello"
是可扩张的,因为可以对它执行这两种扩张操作使得 query = "hello" -> "hellooo" -> "helllllooo" = S
。
输入一组查询单词,输出其中可扩张的单词数量。
样例
输入:
S = "heeellooo"
words = ["hello", "hi", "helo"]
输出:1
解释:
我们能通过扩张 "hello" 的 "e" 和 "o" 来得到 "heeellooo"。
我们不能通过扩张 "helo" 来得到 "heeellooo" 因为 "ll" 的长度小于 3。
限制
0 <= len(S) <= 100
0 <= len(words) <= 100
0 <= len(words[i]) <= 100
S
和所有在words
中的单词都只由小写字母组成。
算法
(暴力枚举,字符串处理) $O(n(L_S + L_w))$
- 暴力枚举判断所有的字符串。
- 判断字符串
a
是否可以扩展成字符串b
- 分别取出一组相同的字符,判断这一组字符是否相同;如果不相同,则返回
false
。 - 如果相同,则判断两组字符的长度,如果
a
的字符组等于b
的字符组,则说明可以扩展;再如果a
的字符组小于b
的字符组且b
的字符组大于等于3
,则也说明可以扩展。 - 否则,不可以扩展,返回
false
。 - 重复以上操作,知道某个字符串先到末尾。
- 分别取出一组相同的字符,判断这一组字符是否相同;如果不相同,则返回
- 最后还需要判断两个字符串的判断是否一起结束。
时间复杂度
- 每个字符串都需要判断一次,判断的时间复杂度为 $O(L_S + L_w)$。
- 故总时间复杂度为 $O(n(L_S + L_w))$。
空间复杂度
- 仅需要常数的额外空间。
C++ 代码
class Solution {
private:
int getNext(const string &a, int st) {
int len = 0;
for (int i = st; i < a.size(); i++) {
if (a[i] == a[st]) len++;
else break;
}
return len;
}
bool check(const string &a, const string &b) {
const int n = a.size(), m = b.size();
int i = 0, j = 0;
while (i < n && j < m) {
if (a[i] != b[j])
return false;
int l1 = getNext(a, i);
int l2 = getNext(b, j);
if (!(l1 == l2 || (l1 < l2 && l2 >= 3)))
return false;
i += l1;
j += l2;
}
return i == n && j == m;
}
public:
int expressiveWords(string S, vector<string>& words) {
int ans = 0;
for (const auto &w : words)
if (check(w, S))
ans++;
return ans;
}
};