Easy
题目描述
句子仅由小写字母(‘a’ 到 ‘z’)、数字(‘0’ 到 ‘9’)、连字符(’-’)、标点符号(’!’、’.’ 和 ‘,’)以及空格(’ ‘)组成。每个句子可以分解成一个或多个由一个或多个空格分隔的标记。
如果一个标记满足下面三个条件,那么它是一个有效单词:
- 仅含有小写字母、连字符和/或标点符号(没有数字)。
- 至多一个 连字符 ‘-’。如果存在,连字符两侧应当有小写字母(“a-b” 是有效的,但是 “-ab” 和 “ab-” 不是有效的)。
- 至多一个 标点符号。如果存在,标点符号应当在标记的 末尾(“ab,"、“cd!” 和 “.” 是有效的,但是 “a!b” 和 “c.,” 不是有效的)。
有效单词的例子包括 “a-b."、“afad”、“ba-c”、“a!” 和 “!"。
给你一个字符串 sentence,请你找出并返回 sentence 中 有效单词的数目。
示例 1:
输入:sentence = "cat and dog"
输出:3
解释:句子中的有效单词是 "cat"、"and" 和 "dog"
示例 2:
输入:sentence = "!this 1-s b8d!"
输出:0
解释:句子中没有有效单词
"!this" 无效,因为它以标点符号开头
"1-s" 和 "b8d" 无效,因为它们含有数字
示例 3:
输入:sentence = "alice and bob are playing stone-game10"
输出:5
解释:句子中的有效单词是 "alice"、"and"、"bob"、"are" 和 "playing"
"stone-game10" 无效,因为它含有数字
提示:
1 <= sentence.length <= 1000sentence仅由小写英文字母、数字、' '、'-'、'!'、'.'和','组成- 至少有 1 个标记
解题思路
这道题要求我们判断句子中有效单词的数量。解题思路如下:
核心思路:
- 首先按空格分割句子,得到各个标记
- 对每个标记进行有效性检查,需要同时满足三个条件
有效单词的检查规则:
- 字符类型检查:只能包含小写字母、连字符、标点符号,不能有数字
- 连字符检查:至多一个连字符,且必须被小写字母包围(不能在开头或结尾)
- 标点符号检查:至多一个标点符号,且必须在末尾
实现细节: 我们可以遍历每个标记的字符,同时统计连字符和标点符号的数量,并检查它们的位置是否合法。对于连字符,需要确保其前后都是小写字母;对于标点符号,需要确保它只出现在末尾。
这是一道典型的字符串模拟题,关键在于仔细处理各种边界情况和约束条件。时间复杂度主要取决于字符串的长度。
代码实现
class Solution {
public:
int countValidWords(string sentence) {
vector<string> tokens;
string token;
// 分割字符串
for (char c : sentence) {
if (c == ' ') {
if (!token.empty()) {
tokens.push_back(token);
token.clear();
}
} else {
token += c;
}
}
if (!token.empty()) {
tokens.push_back(token);
}
int count = 0;
for (const string& word : tokens) {
if (isValidWord(word)) {
count++;
}
}
return count;
}
private:
bool isValidWord(const string& word) {
int hyphenCount = 0;
int punctuationCount = 0;
for (int i = 0; i < word.length(); i++) {
char c = word[i];
if (isdigit(c)) {
return false; // 包含数字
}
if (c == '-') {
hyphenCount++;
if (hyphenCount > 1) return false; // 超过一个连字符
if (i == 0 || i == word.length() - 1) return false; // 连字符在开头或末尾
if (!islower(word[i-1]) || !islower(word[i+1])) return false; // 连字符两侧不是小写字母
}
if (c == '!' || c == '.' || c == ',') {
punctuationCount++;
if (punctuationCount > 1) return false; // 超过一个标点符号
if (i != word.length() - 1) return false; // 标点符号不在末尾
}
}
return true;
}
};
class Solution:
def countValidWords(self, sentence: str) -> int:
tokens = sentence.split()
count = 0
for word in tokens:
if self.isValidWord(word):
count += 1
return count
def isValidWord(self, word: str) -> bool:
hyphen_count = 0
punctuation_count = 0
for i, c in enumerate(word):
if c.isdigit():
return False # 包含数字
if c == '-':
hyphen_count += 1
if hyphen_count > 1:
return False # 超过一个连字符
if i == 0 or i == len(word) - 1:
return False # 连字符在开头或末尾
if not word[i-1].islower() or not word[i+1].islower():
return False # 连字符两侧不是小写字母
if c in '!.,':
punctuation_count += 1
if punctuation_count > 1:
return False # 超过一个标点符号
if i != len(word) - 1:
return False # 标点符号不在末尾
return True
public class Solution {
public int CountValidWords(string sentence) {
string[] tokens = sentence.Split(' ', StringSplitOptions.RemoveEmptyEntries);
int count = 0;
foreach (string word in tokens) {
if (IsValidWord(word)) {
count++;
}
}
return count;
}
private bool IsValidWord(string word) {
int hyphenCount = 0;
int punctuationCount = 0;
for (int i = 0; i < word.Length; i++) {
char c = word[i];
if (char.IsDigit(c)) {
return false; // 包含数字
}
if (c == '-') {
hyphenCount++;
if (hyphenCount > 1) return false; // 超过一个连字符
if (i == 0 || i == word.Length - 1) return false; // 连字符在开头或末尾
if (!char.IsLower(word[i-1]) || !char.IsLower(word[i+1])) return false; // 连字符两侧不是小写字母
}
if (c == '!' || c == '.' || c == ',') {
punctuationCount++;
if (punctuationCount > 1) return false; // 超过一个标点符号
if (i != word.Length - 1) return false; // 标点符号不在末尾
}
}
return true;
}
}
var countValidWords = function(sentence) {
const tokens = sentence.split(/\s+/).filter(token => token.length > 0);
let count = 0;
for (let token of tokens) {
if (isValidWord(token)) {
count++;
}
}
return count;
};
function isValidWord(word) {
let hyphenCount = 0;
let punctuationCount = 0;
for (let i = 0; i < word.length; i++) {
const char = word[i];
if (char >= '0' && char <= '9') {
return false;
}
if (char === '-') {
hyphenCount++;
if (hyphenCount > 1) return false;
if (i === 0 || i === word.length - 1) return false;
if (!(word[i-1] >= 'a' && word[i-1] <= 'z') || !(word[i+1] >= 'a' && word[i+1] <= 'z')) {
return false;
}
}
if (char === '!' || char === '.' || char === ',') {
punctuationCount++;
if (punctuationCount > 1) return false;
if (i !== word.length - 1) return false;
}
}
return true;
}
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | n 为句子长度,需要遍历每个字符进行检查 |
| 空间复杂度 | O(k) | k 为标记数量,用于存储分割后的单词 |