【LeetCode 3136. 有效单词】解析

目录

LeetCode中国站原文

https://leetcode.cn/problems/valid-word/

原始题目

题目描述

有效单词 需要满足以下几个条件:

  1. 至少 包含 3 个字符。
  2. 由数字 0-9 和英文大小写字母组成。(不必包含所有这类字符。)
  3. 至少 包含一个 元音字母
  4. 至少 包含一个 辅音字母

给你一个字符串 word 。如果 word 是一个有效单词,则返回 true ,否则返回 false

注意:

  • 'a''e''i''o''u' 及其大写形式都属于 元音字母
  • 英文中的 辅音字母 是指那些除元音字母之外的字母。

示例 1:

复制代码
输入:word = "234Adas"
输出:true
解释:这个单词满足所有条件。

示例 2:

复制代码
输入:word = "b3"
输出:false
解释:这个单词的长度少于 3 且没有包含元音字母。

示例 3:

复制代码
输入:word = "a3$e"
输出:false
解释:这个单词包含了 '$' 字符且没有包含辅音字母。

提示:

  • 1 <= word.length <= 20

讲解

化繁为简:如何优雅地"盘"逻辑判断题

大家好!今天我们来解决一道非常适合新手入门的题目:LeetCode 3136. 有效单词。

这道题没有炫酷的算法,也不需要复杂的数据结构。它就像一个"逻辑清单",考验的是我们如何把一系列规则,清晰、准确地翻译成代码。这类问题在实际工作中非常常见,比如校验用户输入的密码、验证表单字段等。让我们来看看如何优雅地完成这项任务。

第一部分:算法思想 ------ "清单核对"与"一票否决"

解决这类"多条件判断"问题,最好的方法就是"清单核对法"。我们可以把题目中的所有要求,想象成一张通关清单,我们的 word 字符串需要逐项检查,全部打上勾才能通关。

通关清单:

  1. 长度审查word 的长度是否 >= 3
  2. 成分审查word 中的每一个字符,是否都是字母或数字?
  3. 元音指标word 中是否至少有一个元音字母?
  4. 辅音指标word 中是否至少有一个辅音字母?

核心策略:"一票否决制"

在核对清单时,最有效的策略是"一票否决"。只要在检查过程中发现任何一项不满足,我们就可以立刻得出结论:这个 word 无效,直接返回 false,后面的检查就不用再做了。这可以避免不必要的计算。
"一票否决"流程 否 是 否 是 是 否 是 否 是 否 长度是否 >= 3? 开始 返回 false 遍历每个字符 当前字符是字母或数字? 是元音? 标记: 已找到元音 是辅音? 标记: 已找到辅音 遍历结束 是否同时找到了
元音和辅音? 返回 true

这个流程图清晰地展示了我们的策略:

  1. 先处理硬性条件:首先检查长度和字符合法性。这两项是"全局"的,只要有一个字符不满足,整个单词就作废。
  2. 再统计指标 :在遍历检查字符合法性的同时,我们可以顺便完成"元音"和"辅音"的统计任务。用两个布尔变量 hasVowelhasConsonant 作为标记,一旦找到,就把它设为 true
  3. 最后汇总 :所有字符都遍历完后,如果程序还没有提前退出,说明长度和成分都合格了。这时,我们只需要检查 hasVowelhasConsonant 这两个标记是否都为 true,就能做出最终的裁决。

第二部分:代码实现 ------ 清晰的逻辑翻译

现在,我们把"清单核对"和"一票否决"的策略翻译成代码。

实现一:常规判断逻辑

这是最直接、最易于理解的实现方式。

java 复制代码
class Solution {
    /**
     * 校验一个字符串是否为"有效单词"。
     * @param word 待校验的字符串。
     * @return 如果有效,返回 true;否则返回 false。
     */
    public boolean isValid(String word) {
        // 条件1: 长度审查
        if (word.length() < 3) {
            return false;
        }

        // 准备两个标记,用于统计元音和辅音指标
        boolean hasVowel = false;
        boolean hasConsonant = false;

        // 开始遍历,同时进行"成分审查"和"指标统计"
        for (char ch : word.toCharArray()) {
            // 条件2: 成分审查
            if (!Character.isLetterOrDigit(ch)) {
                return false; // 一票否-决:包含非法字符
            }

            // 判断字符类型,并更新指标
            if (Character.isLetter(ch)) {
                if ("aeiouAEIOU".indexOf(ch) != -1) {
                    hasVowel = true; // 找到了元音
                } else {
                    hasConsonant = true; // 找到了辅音
                }
            }
        }

        // 最终裁决:检查两个核心指标是否都已达成
        return hasVowel && hasConsonant;
    }
}

代码解读:

  • Character.isLetterOrDigit(ch):Java 内置的函数,非常方便地判断一个字符是否为字母或数字。
  • "aeiouAEIOU".indexOf(ch) != -1:这是一个判断元音的小技巧。我们把所有元音字母组成一个字符串,然后检查当前字符 ch 是否能在这个字符串里找到。如果能找到(indexOf 返回的不是 -1),那它就是元音。

实现二:使用正则表达式(一行代码的"炫技")

对于熟悉正则表达式的高手来说,可以用一行代码解决这个问题。这种方法虽然简洁,但在性能上通常不如直接遍历,且可读性对新手不友好,更适合作为一种思路扩展。

java 复制代码
class Solution {
    public boolean isValid(String word) {
        // 正则表达式的含义:
        // (?=.*[aeiouAEIOU])  - 正向先行断言:字符串中必须存在至少一个元音
        // (?=.*[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]) - 字符串中必须存在至少一个辅音
        // [a-zA-Z0-9]{3,}     - 字符串本身必须由3个或以上的大小写字母和数字组成
        return word.matches("(?i)(?=.*[aeiou])(?=.*[b-df-hj-np-tv-z])[a-z0-9]{3,}");
    }
}

注:第二种写法为了简洁,在辅音的判断上使用了[b-df-hj-np-tv-z]这种穷举方式,并使用 (?i) 标志来忽略大小写。这种写法主要是为了展示正则表达式的强大能力,在实际面试中,第一种清晰的遍历写法通常更受青睐。

第三部分:总结

这道"有效单词"题,是一次绝佳的编程基本功练习。它告诉我们:

  • 拆解问题:面对一系列复杂规则,首先要把它们拆解成一个个清晰、独立的小任务。
  • 流程控制:使用"一票否决"可以有效提升代码效率,避免不必要的计算。
  • 善用工具 :熟悉并使用语言内置的函数(如 Character.isLetterOrDigit)可以让代码更简洁、更健壮。