LeetCode 65:有效数字

在这里插入图片描述

问题本质与挑战

需判断字符串是否为有效数字,规则涉及整数、小数、指数(e/E)的复杂组合,如:

  • 整数:+123-45678
  • 小数:1.2.34.5.6
  • 指数:1e102E-33.e4

核心难点:规则分支多(符号、小数点、指数的组合),需用状态机清晰建模所有合法转移。

核心思路:状态机(DFA)

将判断过程拆解为状态转移

  1. 状态定义:每个状态代表当前解析的“阶段”(如“初始状态”“整数部分”“小数部分”等)。
  2. 输入类型:将字符分为 6 类(空格、符号、数字、小数点、指数 e/E、非法字符)。
  3. 转移规则:定义从一个状态到另一个状态的合法转移(如“初始状态”遇到符号→“符号后状态”)。
  4. 终止状态:遍历完字符串后,若处于合法终止状态(如“整数结束”“小数结束”“指数整数结束”),则为有效数字。

状态机设计(详细状态与转移)

1. 状态枚举(10 个核心状态)

enum State {// 初始状态(可空格、符号、数字、小数点)START,// 符号后(需接数字或小数点)SIGN,// 整数部分(可接数字、小数点、指数、空格)INTEGER,// 小数点前无数字(如 .123,需接数字)DOT_WITHOUT_INT,// 小数点后(如 123.,需接数字;或 123.456,可接数字、指数、空格)DOT_WITH_INT,// 指数符号后(需接符号或数字)EXP,// 指数符号后的符号(需接数字)EXP_SIGN,// 指数的整数部分(需接数字、空格)EXP_INT,// 末尾空格(需结束)END_SPACE,// 非法状态(遇到非法字符,直接终止)INVALID
}

2. 输入类型分类(6 类)

private InputType getInputType(char c) {if (c == ' ') return InputType.SPACE;if (c == '+' || c == '-') return InputType.SIGN;if (Character.isDigit(c)) return InputType.DIGIT;if (c == '.') return InputType.DOT;if (c == 'e' || c == 'E') return InputType.EXP;return InputType.INVALID; // 非法字符(如字母、特殊符号)
}

3. 状态转移表(核心逻辑)

用二维数组 trans 定义状态转移规则:trans[当前状态][输入类型] = 下一状态

示例转移(部分关键规则)

  • START 状态遇到 SPACE → 保持 START(前导空格)。
  • START 状态遇到 SIGN → 转移到 SIGN(符号后需接数字或小数点)。
  • INTEGER 状态遇到 DOT → 转移到 DOT_WITH_INT(整数后接小数点,如 123.)。
  • EXP 状态遇到 SIGN → 转移到 EXP_SIGN(指数符号后接符号,如 e-)。

算法步骤详解

步骤 1:初始化状态机

State currentState = State.START; // 初始状态

步骤 2:遍历字符串,逐字符转移状态

for (char c : s.toCharArray()) {InputType type = getInputType(c);currentState = trans[currentState.ordinal()][type.ordinal()]; // 状态转移if (currentState == State.INVALID) break; // 非法状态,提前终止
}

步骤 3:检查终止状态

合法终止状态需满足:

  • INTEGER(纯整数,如 123
  • DOT_WITH_INT(小数,如 123.123.456
  • EXP_INT(指数整数,如 1e10
  • END_SPACE(末尾空格,如 123
return currentState == State.INTEGER || currentState == State.DOT_WITH_INT || currentState == State.EXP_INT || currentState == State.END_SPACE;

完整代码(Java)

class Solution {// 输入类型枚举(空格、符号、数字、小数点、指数、非法)enum InputType { SPACE, SIGN, DIGIT, DOT, EXP, INVALID }// 状态枚举(详细见思路)enum State {START, SIGN, INTEGER, DOT_WITHOUT_INT, DOT_WITH_INT,EXP, EXP_SIGN, EXP_INT, END_SPACE, INVALID}// 状态转移表:[当前状态][输入类型] → 下一状态private final State[][] trans = {// 输入类型顺序:SPACE, SIGN, DIGIT, DOT, EXP, INVALID{State.START,      State.SIGN,     State.INTEGER,    State.DOT_WITHOUT_INT, State.INVALID, State.INVALID}, // START{State.INVALID,    State.INVALID,  State.INTEGER,    State.DOT_WITHOUT_INT, State.INVALID, State.INVALID}, // SIGN{State.END_SPACE,  State.INVALID,  State.INTEGER,    State.DOT_WITH_INT,    State.EXP,     State.INVALID}, // INTEGER{State.INVALID,    State.INVALID,  State.DOT_WITH_INT, State.INVALID,      State.INVALID, State.INVALID}, // DOT_WITHOUT_INT{State.END_SPACE,  State.INVALID,  State.DOT_WITH_INT, State.INVALID,      State.EXP,     State.INVALID}, // DOT_WITH_INT{State.INVALID,    State.EXP_SIGN, State.EXP_INT,     State.INVALID,      State.INVALID, State.INVALID}, // EXP{State.INVALID,    State.INVALID,  State.EXP_INT,     State.INVALID,      State.INVALID, State.INVALID}, // EXP_SIGN{State.END_SPACE,  State.INVALID,  State.EXP_INT,     State.INVALID,      State.INVALID, State.INVALID}, // EXP_INT{State.END_SPACE,  State.INVALID,  State.INVALID,     State.INVALID,      State.INVALID, State.INVALID}, // END_SPACE{State.INVALID,    State.INVALID,  State.INVALID,     State.INVALID,      State.INVALID, State.INVALID}  // INVALID};public boolean isNumber(String s) {State currentState = State.START;for (char c : s.toCharArray()) {InputType type = getInputType(c);currentState = trans[currentState.ordinal()][type.ordinal()];if (currentState == State.INVALID) break; // 非法状态提前终止}// 合法终止状态:整数、小数、指数整数、末尾空格return currentState == State.INTEGER || currentState == State.DOT_WITH_INT || currentState == State.EXP_INT || currentState == State.END_SPACE;}// 将字符分类为输入类型private InputType getInputType(char c) {if (c == ' ') return InputType.SPACE;if (c == '+' || c == '-') return InputType.SIGN;if (Character.isDigit(c)) return InputType.DIGIT;if (c == '.') return InputType.DOT;if (c == 'e' || c == 'E') return InputType.EXP;return InputType.INVALID;}
}

关键代码解释

1. 枚举定义

  • InputType:将字符分类,简化状态转移的条件判断。
  • State:定义解析过程的所有阶段,覆盖合法/非法转移的所有情况。

2. 状态转移表 trans

二维数组存储状态转移规则,trans[当前状态][输入类型] 直接返回下一状态,避免大量 if-else 判断。

3. 主逻辑 isNumber

  • 初始化状态为 START,逐字符处理:
    • getInputType 分类字符,查转移表更新状态。
    • 若遇到非法状态,直接终止遍历。
  • 最后检查是否处于合法终止状态(覆盖所有有效数字的结束情况)。

状态转移示例(以 s = " 123.45e-6 " 为例)

字符输入类型当前状态 → 下一状态解释
SPACESTART → START前导空格,保持初始状态
1DIGITSTART → INTEGER初始状态遇数字,进入整数
2DIGITINTEGER → INTEGER整数部分延续
3DIGITINTEGER → INTEGER整数部分延续
.DOTINTEGER → DOT_WITH_INT整数后接小数点
4DIGITDOT_WITH_INT → DOT_WITH_INT小数部分延续
5DIGITDOT_WITH_INT → DOT_WITH_INT小数部分延续
eEXPDOT_WITH_INT → EXP小数后接指数符号
-SIGNEXP → EXP_SIGN指数符号后接负号
6DIGITEXP_SIGN → EXP_INT负号后接数字,进入指数整数
SPACEEXP_INT → END_SPACE指数整数后接空格

遍历结束后,状态为 END_SPACE,属于合法终止状态 → 有效数字。

复杂度分析

  • 时间复杂度O(n)(遍历字符串一次,状态转移 O(1))。
  • 空间复杂度O(1)(状态和转移表为固定大小)。

总结

状态机是处理复杂规则匹配的“万能钥匙”,通过清晰的状态定义和转移规则,将有效数字的判断转化为状态转移路径的合法性检查。该方法不仅解决问题,还能通过状态枚举覆盖所有边界情况(如 .11.1e10 等),是字符串解析类问题的经典解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91745.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91745.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/91745.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之顺序表应用与双指针法

元素删除通过元素移动的方式来模拟删除操作:将指定下标后的所有元素依次向前移动一位,覆盖要删除的元素,从而达到 "删除" 的效果。 通过自定义函数实现删除功能,需要传入数组、数组长度的指针(因为要修改长度…

Python编程基础与实践:Python基础数据类型入门

Python变量与数据类型实践 学习目标 通过本课程的学习,学员可以掌握Python中变量的基本概念,了解并能够使用Python的基本数据类型,包括整型、浮点型、字符串和布尔值。此外,学员还将学习如何在实际编程中声明和使用这些数据类型。…

深入解析C/C++函数变量传递:栈、堆与全局变量的生命周期之旅

资料合集下载链接: ​https://pan.quark.cn/s/472bbdfcd014​ 在编程学习中,函数是构建程序的基石,而理解变量如何在函数之间正确、安全地传递,则是从入门到进阶的关键一步。我们经常会遇到这样的困惑:为什么一个指针在某个函数里工作正常,传递给另一个函数后却变成了“…

Ubuntu18网络连接不上也ping不通网络配置问题排查与解决方法

Ubuntu 18启动以后发现连接不上网络,执行 ip a命令或者ifconfig都显示不了正确的地址(192.168.xxx.xxx)。 刚装好系统是没问题的,打算使用FTP开启ftp服务与windows互传文件,安装了net-tools插件就突然连不上网络了,怀疑是网络配置被修改了。 经过了一段时间折腾终于解决了,…

【计算机网络】Socket网络编程

目录 一、主机字节序列和网络字节序列 二、套接字地址结构 1、IPv4 地址结构 (sockaddr_in) 2、IPv6 地址结构 (sockaddr_in6) 3、通用套接字地址结构 (sockaddr) 4、Unix域套接字地址结构 (sockaddr_un) 5、专用 socket 地址结构 6、套接字地址结构的转换 字符串转二进制地址 …

网页操作自动化解决方案:如何用Browser-Use+CPolar提升企业运营效率

文章目录前言1. 安装Ollama2. Gemma3模型安装与运行3. 虚拟环境准备3.1 安装Python3.2. 安装conda4. 本地部署Brower Use WebUI4.1 创建一个新conda环境4.2 克隆存储库4.3 安装依赖环境4.4 安装浏览器自动化工具4.5 修改配置信息5. 本地运行测试6. 安装内网穿透6.1 配置公网地址…

Pycharm的设置过程

20250802 用于记录pycharm的设置过程 编辑器相关 python语言设置文件注释 在设置的编辑器部分,按照需求设置模板! 函数生成注释

GaussDB as的用法

通过使用 SQL,可以为表名称或列名称指定别名(Alias)。1 别名的作用SQL 别名用于为表或表中的列提供临时名称。 SQL 别名通常用于使列名更具可读性。 SQL 一个别名只存在于查询期间。 提高SQL执行效率与编写SQL代码效率。2 使用别名的场景在下…

Prim算法

一,prim算法逻辑1.理解:克鲁斯卡尔算法关注的是边,普里姆算法关注的是点把图中每个顶点比作孤岛,点亮一座孤岛就可以解锁附近的孤岛每次解锁的点都是离自身最近的点2.普里姆算法流程a.采用邻接矩阵表示,考虑要查找最小…

嵌入式学习之硬件——51单片机 1.0

一、基础知识1.什么是嵌入式?嵌入式以应用为中心,计算机技术为基础,软硬件可裁剪的专用计算机系统;2.嵌入式的应用?消费电子、无人驾驶、储能、新能源........3.嵌入式发展?(1)第一阶…

51c大模型~合集161

自己的原文哦~ https://blog.51cto.com/whaosoft/14079111 #这家国内公司,在给xx智能技术栈做「通解」 打通机器人智能化的关键:眼脑手。 xx智能(Embodied Intelligence)是 AI 领域里热度极高的赛道:给大模型…

Linux9 root密码修改

开机按e进入在linux行即quiet后面输入rd.break ctrlx进入内核输入mount -o remount,rw /sysrootchroot /sysrootpasswd root即可修改密码输入touch /.autorelabelexitexit等待即可

提示词增强工程(Prompt Enhancement Engineering)白皮书草稿

提示词增强工程(Prompt Enhancement Engineering)白皮书草稿 作者: 技术人进化社 Email:2819699195qq.com 日期: 2025年7月30日 1. 引言 随着大型语言模型(LLM)能力的飞速发展,如何高…

电路元器件

电流单位 电压 电阻单位 电阻的决定式 欧姆定律 交流电和直流电 交流电 串联电路 并联电路 在线模拟器 Circuitjs web 在线电路模拟器 下载

广泛分布于内侧内嗅皮层全层的速度细胞(speed cells)对NLP中的深层语义分析的积极影响和启示

速度细胞(Speed Cells)作为内侧内嗅皮层(MEC)的核心神经元,通过编码运动速度信息与网格细胞协同实现动态路径整合。这一神经机制为自然语言处理(NLP)的深层语义分析提供了以下关键启示和影响&am…

sql中的多表查询

在SQL中,多表查询用于从多个表中组合数据,常见的方法包括 ​连接查询(JOIN)​​ 和 ​子查询。以下是详细说明和示例:一、连接查询(JOIN)通过关联字段将多个表的数据合并,分为以下几…

Ruby 面向对象编程深入解析

Ruby 面向对象编程深入解析 引言 Ruby 作为一种动态、解释型、面向对象的语言,自1995年由日本程序员Yukihiro Matsumoto创造以来,凭借其简洁、灵活和强大的面向对象特性,在全球范围内获得了广泛的认可。本文将深入探讨Ruby的面向对象编程(OOP)特性,帮助读者更好地理解和…

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现围栏羊驼的检测识别(C#代码,UI界面版)

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现围栏羊驼的检测识别(C#代码,UI界面版)工业相机使用YoloV8模型实现围栏羊驼的检测识别工业相机通过YoloV8模型实现围栏羊驼的检测识别的技术背景在相机SDK中获取图像转换图像的代码分…

如何利用 rowid 在OceanBase 中处理大表时提效

本文作者:张瑞远,现主要从事电信级IT系统及数据库的规划设计、架构设计、运维实施、运维服务、故障处理、性能优化等工作,曾经从事银行、证券数仓设计、开发、优化类工作,持有Orale OCM,MySQL OCP及国产代表数据库认证。 获得包括…

【从0开始学习Java | 第4篇】类和对象

文章目录👏类和对象的概念什么是类?什么是对象?🥝构造方法如何创建一个对象?🥝对象内存布局完整应用 - 编写一个类:人,其具备年龄、性别、姓名等基础属性,并实例化一个人…