表示数值的字符串(有限状态自动机与搜索)

文章目录

  • 题目
  • 思路一
  • 代码一
  • 思路二
  • 代码二


题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路一

考察有限状态自动机(参考jyd):

字符类型:

空格 「 」、数字「 0—9 」 、正负号 「 ± 」 、小数点 「 . 」 、幂符号 「 eE 」 。

状态定义:

按照字符串从左到右的顺序,定义以下 9 种状态:

  1. 开始的空格
  2. 幂符号前的正负号
  3. 小数点前的数字
  4. 小数点、小数点后的数字
  5. 当小数点前为空格时,小数点、小数点后的数字
  6. 幂符号
  7. 幂符号后的正负号
  8. 幂符号后的数字
  9. 结尾的空格

结束状态:

合法的结束状态有 2, 3, 7, 8 。
在这里插入图片描述
算法流程:

  1. 初始化:

    1. 状态转移表 maps : 设 maps[i] ,其中 i 为所处状态(9种之一), maps[i] 使用哈希表存储可转移至的状态。键值对 (key, value) 含义:若此时的字符是 key ,则可从状态 i 转移至状态 value 。
    2. 当前状态 p : 起始状态初始化为 p = 0 。
  2. 状态转移循环: 遍历字符串 s 的每个字符 c 。

    1. 记录字符类型 t : 分为四种情况。

      • 当 c 为正负号时,执行 t = 's' ;
      • 当 c 为数字时,执行 t = 'd' ;
      • 当 c 为 e , E 时,执行 t = 'e' ;
      • 当 c 为 . , 空格 时,执行 t = c (即用字符本身表示字符类型);
      • 否则,执行 t = ‘?’ ,代表为不属于判断范围的非法字符,后续直接返回 false。
    2. 终止条件: 若字符类型 t 不在哈希表 maps[p] 中,说明无法转移至下一状态,因此直接返回 False 。

    3. 状态转移: 状态 p 转移至 maps[p][t]

  3. 返回值: 跳出循环后,若状态 p∈2,3,7,8 ,说明结尾合法,返回 True ,否则返回 False 。

再详细说一下状态转移表的作用(下面代码部分的注释中有结合实例进行详解):

  • 处于第 i 行表明此时遍历到的字符是第 i+1 种状态(可以对照上文的状态定义)
  • 那么我下一个字符可以是第 i 行中的各个 key 对应的字符。
  • 如果某字符没有写进第 i 行,表示 i+1 状态的下一个字符不应是某字符

复杂度分析:

  1. 时间复杂度 O(N) : 其中 N 为字符串 s 的长度,判断需遍历字符串,每轮状态转移的使用 O(1) 时间。
  2. 空间复杂度 O(1) : maps 和 p 使用常数大小的额外空间。

代码一

class Solution {
public:bool isNumber(string s) {vector<map<char,int>> maps = {
// 状态转移表
// 以第一行为例,状态转移表的含义为:
// 开始的空格其下一个字符可以继续是空格,也可以是符号、数字、小数点,
// 但不可是第0行不存在的e。
// 也就是为了保证字符串是数值,空格后面不能直接跟一个幂符号。{{' ', 0},  {'s', 1},  {'d', 2},  {'.', 4}}, // 开始的空格{{'d', 2}, {'.', 4}}, // 幂符号前的正负号{{'d', 2}, {'.', 3}, {'e', 5}, {' ', 8}}, // 小数点前的数字{{'d', 3}, {'e', 5}, {' ', 8}}, // 小数点、小数点后的数字{{'d', 3}}, // 当小数点前为空格时,小数点、小数点后的数字{{'s', 6}, {'d', 7}}, // 幂符号{{'d', 7}}, // 幂符号后的正负号{{'d', 7}, {' ', 8}}, // 幂符号后的数字{{' ', 8}} // 结尾的空格};int p = 0; //起始状态char t;for(char c : s) {if(c >= '0' && c <= '9') t = 'd';else if(c == '+' || c == '-') t = 's';else if(c == 'e' || c == 'E') t = 'e';else if(c == '.' || c == ' ') t = c;else t = '?';if(maps[p].find(t) == maps[p].end()) return false;p = maps[p][t];}return p == 2 || p == 3 || p == 7 || p == 8;}
};

思路二

用 bool 类型变量 ret 存储搜索过程中的结果,整个搜索过程如下:

  1. 过滤首部空格
  2. 过滤正负号,紧接着检查是否有数字
  3. 遇到第一个不是数字的字符,应为 '.' 或者 e
  4. '.' 的后面可以有 e,但 e 的后面不能有 '.' ,所以先处理 '.' 再处理 e
  5. '.' 前面可以什么都没有,后面也可以什么都没有,只要有一边有数据就行
  6. e 的前后必须都要有数据,并且后面可以存在正负号,所以需要过滤正负号
  7. 过滤尾部空格
  8. 检查 ret 状态以及是否已经遍历完字符串

关于第8点检查是否已经遍历完字符串:

因为如果字符串是数值,尾部空格应该是字符串的末尾部分倒数几个字符,空格完了字符串应该也就结束了。如果过滤完了尾部空格还有字符,说明该字符串不是数值。

代码二

class Solution {
public:bool scanDigit(string s, int& i){int count = i;while(i != s.size()){if(isdigit(s[i]))++i;elsebreak;}return i > count;//如果数据中没有一个数字,则直接返回false,有则返回true}bool scanSign(string s, int& i){if(i < s.size() && s[i] == '+' || s[i] == '-'){++i;}//过滤正负号return scanDigit(s, i);}bool isNumber(string s) {if(s.empty())return false;int i = 0;while(s[i] == ' ')++i;//过滤首部空格bool ret = scanSign(s, i);//第一遍搜索,先走完.或者e前的所有数字//.的后面可以有e,但e的后面不能有.,所以先处理.再处理eif(i < s.size() && s[i] == '.'){ret = scanDigit(s, ++i) || ret;//.前面可以什么都没有,后面也可以什么都没有,只要有一边有数据就行}if(i < s.size() && (s[i] == 'e' || s[i] == 'E')){ret = scanSign(s, ++i) && ret;//e的前后必须都要有数据,并且e后面可以存在正负号,所以需要过滤正负号}while(s[i] == ' ')++i;//因为空格只能出现在末尾和首部,过滤空格return ret && (i == s.size());//当e后面有数据,并且字符串全部走完,说明数据成立}
};

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

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

相关文章

树的子结构

文章目录题目深搜深搜代码广搜广搜代码题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 给定的树 B&#xff1a; 返回 true&#xff0c;因为…

写题过程中碰见的小问题

文章目录和--vector二维vector的初始化数组中最大的数max_element()数组所有元素之和accumulate()vector数组去重对pair类型的vector排序对元素都为正整数的vector利用sort默认的升序排列进行降序排列一维数组转二维数组size_t和int如何不用临时变量交换两个数?将类函数的形参…

LeetCode——二叉树序列化与反序列化

文章目录题目思路问题一问题二代码实现题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 设计一个算法来实现二叉树的序列化与反序列化。不限定序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序…

jsp中生成的验证码和存在session里面的验证码不一致的处理

今天在调试项目的时候发现&#xff0c;在提交表单的时候的验证码有问题&#xff0c;问题是这样的&#xff1a;就是通过debug模式查看得知&#xff1a;jsp页面生成的验证码和表单输入的页面输入的一样&#xff0c;但是到后台执行的时候&#xff0c;你会发现他们是不一样的&#…

求1~n这n个整数十进制表示中1出现的次数

文章目录题目思路代码复杂度分析题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;那么1&#xff5e;12这些整数中包含1 的数字有1、10、11和12。可得1一共出现了5次。 思路 将个位、十位……每位…

求数字序列中的第n位对应的数字

文章目录题目思路代码复杂度分析致谢题目 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中&#xff0c;第5位&#xff08;从下标0开始计数&#xff09;是5&#xff0c;第13位是1&#xff0c;第19位是4&#xff0c;等等。 请写一个函数&#xff0c…

一学就废的归并排序

文章目录其他与排序有关的文章原理代码实现复杂度分析其他与排序有关的文章 一学就废的三种简单排序【冒泡、插入、选择】 原理 归并排序&#xff08;Merge sort&#xff09;&#xff1a; 归并排序对元素 递归地 进行 逐层折半分组&#xff0c;然后从最小分组开始&#xff0c…

树状数组的相关知识 及 求逆序对的运用

文章目录树状数组概念前缀和和区间和树状数组原理区间和——单点更新前缀和——区间查询完整代码离散化sort函数unique函数去重erase函数仅保留不重复元素通过树状数组求逆序对树状数组概念 树状数组又名二叉索引树&#xff0c;其查询与插入的复杂度都为 O(logN)&#xff0c;其…

二叉搜索树相关知识及应用操作

文章目录概念查找二叉搜索树的第k大节点概念 二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又名&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;——它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若它的…

二叉树相关知识及求深度的代码实现

文章目录树二叉树满二叉树和完全二叉树二叉树的性质代码实现求二叉树的深度树 树是一种非线性的数据结构&#xff0c;它是由n个有限结点组成一个具有层次关系的集合。 树的相关名词&#xff1a; 根节点&#xff1a;没有前驱结点的结点。父节点&#xff0c;子节点&#xff1a…

大端小端存储模式详解及判断方法

文章目录大小端模式的概念两种模式出现原因两种模式的优劣大小端的应用情景判断机器的字节序大小端模式的概念 当我们查看数据在内存中的存储情况时&#xff0c;我们经常会发现一个很奇怪的现象&#xff0c;什么现象呢&#xff1f; int main() {int i 12;return 0; }数据在内…

Linux 内存管理 | 物理内存、内存碎片、伙伴系统、SLAB分配器

文章目录物理内存物理内存分配外部碎片内部碎片伙伴系统(buddy system)slab分配器物理内存 在Linux中&#xff0c;内核将物理内存划分为三个区域。 在解释DMA内存区域之前解释一下什么是DMA&#xff1a; DMA&#xff08;直接存储器访问&#xff09; 使用物理地址访问内存&am…

Linux 内存管理 | 虚拟内存管理:虚拟内存空间、虚拟内存分配

文章目录虚拟地址空间用户空间内核空间用户空间内存分配malloc内核空间内存分配kmallocvmalloc虚拟地址空间 在早期的计算机中&#xff0c;程序是直接运行在物理内存上的&#xff0c;而直接使用物理内存&#xff0c;通常都会面临以下几种问题&#xff1a; 内存缺乏访问控制&a…

Linux | 编译原理、gcc的命令参数、自动化构建工具 make/Makefile

文章目录编译原理预处理编译汇编链接gcc的常用命令参数make 和 Makefile 的概念make的运行通配符自动化变量伪目标.PHONE:【命令】编译原理 在解释 makefile 前&#xff0c;首先解释一下 .c 文件变成 .exe 文件要经过的四个步骤——预处理、编译、汇编和链接&#xff08;参考来…

Linux | 进程概念、进程状态(僵尸进程、孤儿进程、守护进程)、进程地址空间

文章目录进程和程序操作系统如何控制和调度程序进程控制块–PCB子进程进程状态僵尸进程孤儿进程守护进程&#xff08;精灵进程&#xff09;进程地址空间引言页表进程和程序 程序&#xff1a; 一系列有序的指令集合&#xff08;就是我们写的代码&#xff09;。进程&#xff1a;…

Linux 进程控制 :进程创建,进程终止,进程等待,程序替换

文章目录进程创建进程等待程序替换进程终止进程创建 fork函数&#xff1a; 操作系统提供的创建新进程的方法&#xff0c;父进程通过调用 fork函数 创建一个子进程&#xff0c;父子进程代码共享&#xff0c;数据独有。 当调用 fork函数 时&#xff0c;通过 写时拷贝技术 来拷贝…

Linux 内存管理 | 连续分配方式 和 离散分配方式

文章目录前言连续分配单一连续分配分区式分配固定分区分配动态分区分配可重定位分区分配离散分配分段分页多级页表快表(TLB)段页式Linux前言 Linux 内存管理 | 虚拟内存管理&#xff1a;虚拟内存空间、虚拟内存分配 Linux 内存管理 | 物理内存、内存碎片、伙伴系统、SLAB分配器…

操作系统 | 用户态和内核态的切换(中断、系统调用与过程(库函数)调用)

文章目录中断过程调用系统调用过程调用和系统调用的区别中断 用户态、内核态之间的切换是怎么实现的? 用户态→内核态 是通过中断实现的。并且 中断是唯一途径 。核心态→用户态 的切换是通过执行一个特权指令&#xff0c;将程序状态字 (PSW) 的标志位设置为 用户态 。 中断…

管道实现父子进程的信息传递(二)【标准流和其文件描述符、fwrite函数、perror函数】

文章目录代码实现标准流 和 标准流文件描述符代码中用到的函数fwrite()perror()在复习进程间的通信方式时又写了一遍&#xff0c;和 管道实现父子进程的信息传递&#xff08;一&#xff09;【fork函数、pipe函数、write/read操作、wait函数】 的区别不是特别大&#xff0c;只是…

命名管道实现进程的信息传递【mkfifo函数、open函数】

文章目录代码实现mkfifo函数open函数代码实现 #include<fcntl.h> // open() #include<sys/wait.h> // wait() #include<sys/types.h> // mkfifo() #include<sys/stat.h> // mkfifo() #include<iostream> #include<unistd.h> // fork()usi…