题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
数值(按顺序)可以分成以下几个部分:
- 若干空格
- 一个 小数 或者 整数
- (可选)一个
'e'
或'E'
,后面跟着一个 整数- 若干空格
小数(按顺序)可以分成以下几个部分:
- (可选)一个符号字符(
'+'
或'-'
)- 下述格式之一:
- 至少一位数字,后面跟着一个点
'.'
- 至少一位数字,后面跟着一个点
'.'
,后面再跟着至少一位数字- 一个点
'.'
,后面跟着至少一位数字整数(按顺序)可以分成以下几个部分:
- (可选)一个符号字符(
'+'
或'-'
)- 至少一位数字
有限状态自动机
针对类似这一类字符串识别的题目,通过建立有限状态自动机(FSM)模型,可以有效解决这一类问题。
在计算机中,有限状态机通常用于描述程序的执行过程。每个状态表示程序的某个特定的执行状态,例如等待输入、执行计算、输出结果等。状态之间的转换表示程序在不同状态之间的切换,例如根据输入继续进行条件判断、执行循环等。
有限状态自动机的状态转换是通过输入符号触发的,这一点类似于unity引擎的动画状态机Animator。通过键盘输入等方式转变当前GameObject的状态,对象可能永远停留在当前状态,也有可能结束当前的流程。
自动机包含一系列的状态,在这些状态中:
- 有一种特殊的状态,称为初始状态。
- 还有一系列状态被称为接受状态。
起初,自动机处于初始状态,随后根据输入的信息,按照实现约定好的转移规则,从当前状态转移到下一个状态,当字符串全部读取完毕后,如果自动机处于某个接受状态,则判定该输入被接受;否则,判定该字符串被拒绝。
如果在输入过程中,接收到不符合约定好的转移规则的输入,此时计算将提前终止,判定该输入被拒绝。
自动机驱动的编程,可以被看做一种暴力枚举方法的延伸:它穷尽了在任何一种情况下,对应任何的输入,需要做的事情。
FSM解题的步骤
- 确定输入集
- 确定状态集合,并对状态进行分类
- 确定状态转移函数并作图
运用FSM解该题
1. 确定输入集
- 空格~' '
- +/-号~symbol
- 数字~number
- 点号~'.'
- 指数符号e/E~e
2. 确定状态并分类
- 初始状态~Start
- 符号位1~Symbol1
- 点号位1~Point1
- 整数位1~Number1(接受状态)
- 点号位2~Symbol2(接受状态)
- 整数位2~Number2(接收状态)
- 指数符号部分~E
- 符号位2~Symbol2
- 整数位3~Number3(接受状态)
- 空格位~Space(接受状态)
3. 确定状态转移函数并作图
状态机示意图
4. 根据画出的状态机图写出代码
class Solution {
//将状态设置为枚举类型
enum State {
Start,
Symbol1,
Point1,
Number1,
Point2,
Number2,
E,
Symbol2,
Number3,
Space,
};
public bool isNumeric (string str) {
if (string.IsNullOrEmpty(str)) {
return false;
}
//设置输入集
string number = "0123456789";
string symbol = "+-";
string e = "eE";
State state = State.Start;
foreach (char i in str) {//利用foreach循环遍历字符串
//利用switch语句实现状态机
switch (state) {
case State.Start:
if (' '.Equals(i)) state = State.Start;
else if (symbol.Contains(i)) state = State.Symbol1;
else if (number.Contains(i)) state = State.Number1;
else if ('.'.Equals(i)) state = State.Point1;
else return false;
break;
case State.Symbol1:
if (number.Contains(i)) state = State.Number1;
else if ('.'.Equals(i)) state = State.Point1;
else return false;
break;
case State.Point1:
if (number.Contains(i)) state = State.Number2;
else return false;
break;
case State.Number1:
if (number.Contains(i)) state = State.Number1;
else if ('.'.Equals(i)) state = State.Point2;
else if (e.Contains(i)) state = State.E;
else if (' '.Equals(i)) state = State.Space;
else return false;
break;
case State.Point2:
if (number.Contains(i)) state = State.Number2;
else if (e.Contains(i)) state = State.E;
else if (' '.Equals(i)) state = State.Space;
else return false;
break;
case State.Number2:
if (number.Contains(i)) state = State.Number2;
else if (e.Contains(i)) state = State.E;
else if (' '.Equals(i)) state = State.Space;
else return false;
break;
case State.E:
if (symbol.Contains(i)) state = State.Symbol2;
else if (number.Contains(i)) state = State.Number3;
else return false;
break;
case State.Symbol2:
if (number.Contains(i)) state = State.Number3;
else return false;
break;
case State.Number3:
if (number.Contains(i)) state = State.Number3;
else if (' '.Equals(i)) state = State.Space;
else return false;
break;
case State.Space:
if (' '.Equals(i)) state = State.Space;
else return false;
break;
}
}
//判断非接受状态
if (state == State.Start || state == State.Symbol1 || state == State.Point1 ||
state == State.E || state == State.Symbol2) {
return false;
}
return true;
}
}