|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[2047. 句子中的有效单词数](https://leetcode-cn.com/problems/number-of-valid-words-in-a-sentence/solution/gong-shui-san-xie-jian-dan-zi-fu-chuan-m-5pcz/)** ,难度为 **简单**。 |
| 4 | + |
| 5 | +Tag : 「模拟」、「双指针」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +句子仅由小写字母(`'a'` 到 `'z'`)、数字('0' 到 '9')、连字符(`'-'`)、标点符号(`'!'`、`'.'` 和 `','`)以及空格(`' '`)组成。每个句子可以根据空格分解成 一个或者多个 `token` ,这些 `token` 之间由一个或者多个空格 `' '` 分隔。 |
| 10 | + |
| 11 | +如果一个 `token` 同时满足下述条件,则认为这个 `token` 是一个有效单词: |
| 12 | + |
| 13 | +* 仅由小写字母、连字符和/或标点(不含数字)。 |
| 14 | +* 至多一个 连字符 `'-'` 。如果存在,连字符两侧应当都存在小写字母(`"a-b"` 是一个有效单词,但 `"-ab"` 和 `"ab-"` 不是有效单词)。 |
| 15 | +* 至多一个 标点符号。如果存在,标点符号应当位于 `token` 的 末尾 。 |
| 16 | +这里给出几个有效单词的例子:`"a-b."`、`"afad"`、`"ba-c"`、`"a!"` 和 `"!"` 。 |
| 17 | + |
| 18 | +给你一个字符串 `sentence` ,请你找出并返回 `sentence` 中 有效单词的数目 。 |
| 19 | + |
| 20 | +示例 1: |
| 21 | +``` |
| 22 | +输入:sentence = "cat and dog" |
| 23 | + |
| 24 | +输出:3 |
| 25 | + |
| 26 | +解释:句子中的有效单词是 "cat"、"and" 和 "dog" |
| 27 | +``` |
| 28 | +示例 2: |
| 29 | +``` |
| 30 | +输入:sentence = "!this 1-s b8d!" |
| 31 | + |
| 32 | +输出:0 |
| 33 | + |
| 34 | +解释:句子中没有有效单词 |
| 35 | +"!this" 不是有效单词,因为它以一个标点开头 |
| 36 | +"1-s" 和 "b8d" 也不是有效单词,因为它们都包含数字 |
| 37 | +``` |
| 38 | +示例 3: |
| 39 | +``` |
| 40 | +输入:sentence = "alice and bob are playing stone-game10" |
| 41 | + |
| 42 | +输出:5 |
| 43 | + |
| 44 | +解释:句子中的有效单词是 "alice"、"and"、"bob"、"are" 和 "playing" |
| 45 | +"stone-game10" 不是有效单词,因为它含有数字 |
| 46 | +``` |
| 47 | +示例 4: |
| 48 | +``` |
| 49 | +输入:sentence = "he bought 2 pencils, 3 erasers, and 1 pencil-sharpener." |
| 50 | + |
| 51 | +输出:6 |
| 52 | + |
| 53 | +解释:句子中的有效单词是 "he"、"bought"、"pencils,"、"erasers,"、"and" 和 "pencil-sharpener." |
| 54 | +``` |
| 55 | + |
| 56 | +提示: |
| 57 | +* 1ドル <= sentence.length <= 1000$ |
| 58 | +* `sentence` 由小写英文字母、数字(`0-9`)、以及字符(`' '`、`'-'`、`'!'`、`'.'` 和 ',')组成 |
| 59 | +* 句子中至少有 1ドル$ 个 `token` |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +### 模拟 |
| 64 | + |
| 65 | +根据题意进行模拟即可,先将 `sentence` 按照空格进行分割,得到多个 `item`,再对每个 `item` 进行合法性检查,最后统计合法的 `item` 个数即为答案。 |
| 66 | + |
| 67 | +在对 `item` 进行合法性检查时,分别使用 $c1$ 和 $c2$ 代表「连字符」和「标点符号」的出现次数。 |
| 68 | + |
| 69 | +> 特别说明:Java 的 `split` 操作基于正则,复杂度为线性,其他语言可能需要使用「双指针」手写 `split` 操作 |
| 70 | + |
| 71 | +代码( 双指针实现 `split` 的代码见 $P2$ ): |
| 72 | +```Java |
| 73 | +class Solution { |
| 74 | + public int countValidWords(String sentence) { |
| 75 | + String[] ss = sentence.split(" "); |
| 76 | + int ans = 0; |
| 77 | + for (String s : ss) if (check(s)) ans++; |
| 78 | + return ans; |
| 79 | + } |
| 80 | + boolean check(String s) { |
| 81 | + int n = s.length(); |
| 82 | + if (n == 0) return false; |
| 83 | + for (int i = 0, c1 = 0, c2 = 0; i < n; i++) { |
| 84 | + char c = s.charAt(i); |
| 85 | + if (Character.isDigit(c)) return false; |
| 86 | + if (c == ' ') return false; |
| 87 | + if (c == '-' && ++c1 >= 0) { |
| 88 | + if (c1 > 1 || (i == 0 || i == n - 1)) return false; |
| 89 | + if (!Character.isLetter(s.charAt(i - 1)) || !Character.isLetter(s.charAt(i + 1))) return false; |
| 90 | + } |
| 91 | + if ((c == '!' || c == '.' || c == ',') && ++c2 >= 0) { |
| 92 | + if (c2 > 1 || (i != n - 1)) return false; |
| 93 | + } |
| 94 | + } |
| 95 | + return true; |
| 96 | + } |
| 97 | +} |
| 98 | +``` |
| 99 | +- |
| 100 | +```Java |
| 101 | +class Solution { |
| 102 | + public int countValidWords(String sentence) { |
| 103 | + int n = sentence.length(), ans = 0; |
| 104 | + for (int i = 0; i < n; ) { |
| 105 | + if (sentence.charAt(i) == ' ' && ++i >= 0) continue; |
| 106 | + int j = i; |
| 107 | + while (j < n && sentence.charAt(j) != ' ') j++; |
| 108 | + if (check(sentence.substring(i, j))) ans++; |
| 109 | + i = j + 1; |
| 110 | + } |
| 111 | + return ans; |
| 112 | + } |
| 113 | + boolean check(String s) { |
| 114 | + int n = s.length(); |
| 115 | + if (n == 0) return false; |
| 116 | + for (int i = 0, c1 = 0, c2 = 0; i < n; i++) { |
| 117 | + char c = s.charAt(i); |
| 118 | + if (Character.isDigit(c)) return false; |
| 119 | + if (c == ' ') return false; |
| 120 | + if (c == '-' && ++c1 >= 0) { |
| 121 | + if (c1 > 1 || (i == 0 || i == n - 1)) return false; |
| 122 | + if (!Character.isLetter(s.charAt(i - 1)) || !Character.isLetter(s.charAt(i + 1))) return false; |
| 123 | + } |
| 124 | + if ((c == '!' || c == '.' || c == ',') && ++c2 >= 0) { |
| 125 | + if (c2 > 1 || (i != n - 1)) return false; |
| 126 | + } |
| 127 | + } |
| 128 | + return true; |
| 129 | + } |
| 130 | +} |
| 131 | +``` |
| 132 | +* 时间复杂度:$O(n)$ |
| 133 | +* 空间复杂度:执行过程会产生若干子串,子串总长度与 `s` 相同。复杂度为 $O(n)$ |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +### 最后 |
| 138 | + |
| 139 | +这是我们「刷穿 LeetCode」系列文章的第 `No.2047` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 140 | + |
| 141 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 142 | + |
| 143 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 144 | + |
| 145 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 146 | + |
0 commit comments