有限状态机
先看一道恶心的面试题:
最简单的思路:对字符串进行遍历,分别对遇到的字符串进行分情况讨论。例如对于一些简单字符串, 如只有数字和正负号还有不合规字符的”+100, -123sdjhg“,我们可以写下如下代码:
function isNumber(s:string){
for(let i=0; i<s.length; i++){
const c = s[i]
if('+-'.includes(c)){
if(i>0) return false
} else if(/[0-9]/.test(c)){
continue
} else {
return false
}
}
return true
}
然而,在这个题目里,要求判断的数字,我们面临着更多的情况。如:开头可以是空格,可以是”+-”, 还可以是数字、小数点;数字后面可以跟小数点、e、数字;结尾可以是空格、数字、或者小数点。并且对于不同情况下的小数点,后面可以跟的东西又不同,如’.’不可以是一个数字,’3.‘就是一个数字。小数点前的数字和小数点后的数字能够跟的东西都不相同了。想一想这些东西排列组合一下,可以组合出多少种情况,如果用if-elseif-else去写,不知道要写到猴年马月,并且难免会有一些遗漏。
这时候,有限状态机就出来帮助你啦。什么是有限状态机呢?首先看状态,状态在这个题目就是字符串里的每一个字符所处的场景,也就是if-else里的if,也就是现在读到的字符后面可以跟哪些东西。把这个状态写成一个表,通过它,你可以对上述这些情况分别定义状态,每读到一个字符,就根据它的类型去判断它所在的状态,就是判断它后面可以跟那些东西,如果没有状态,那就是判断失败,就可以得出结论了。
直接上代码:
function isNumber(s: string): boolean {
// 状态 0 空格 1 数字
// 类型 sign, num, dot, e,
const states = [
{
num: 1,
sign: 8,
dot: 9,
blank: 0
}, // 开头的空格
{
dot: 3,
num: 1,
e: 4,
blank: 6
}, // 点前的数字
{
e: 4,
num: 2,
blank: 6
}, //点后的数字
{
num: 2,
blank: 6,
e: 4
}, // 点
{
num: 5,
sign: 7
},// e
{
num: 5,
blank: 6
}, // e后的数字
{
blank: 6
}, //结尾的空格
{
num: 5
}, // e后面的正负号
{
num: 1,
dot: 9
}, // 正负号
{
num: 2
}
]
let t = "blank"
let p = 0
for(const c of s){
if(/[0-9]/.test(c)){
t = 'num'
} else if (c===' '){
t = 'blank'
} else if ('+-'.includes(c)){
t = 'sign'
} else if ('eE'.includes(c)){
t = 'e'
} else if (c==='.'){
t = 'dot'
} else t = 's'
p = states[p][t]
console.log(p,c,t)
if(!p&&p!==0){
return false
}
}
return [6,1,2,3,5].includes(p)
};
启示:对于一种情况决定另一种情况,并且前后有多种情况依次关联的题目类型,应采用有限状态机,可以减少if-else的使用,保证触达所有场景。