題目描述
請實作一個函數用來判斷字元串是否表示數值(包括整數和小數)。
數值(按順序)可以分成以下幾個部分:
- 若幹空格
- 一個 小數 或者 整數
- (可選)一個 ‘e’ 或 ‘E’ ,後面跟着一個 整數
- 若幹空格
小數(按順序)可以分成以下幾個部分:
- (可選)一個符号字元(’+’ 或 ‘-’)
- 下述格式之一:
- 至少一位數字,後面跟着一個點 ‘.’
- 至少一位數字,後面跟着一個點 ‘.’ ,後面再跟着至少一位數字
- 一個點 ‘.’ ,後面跟着至少一位數字
整數(按順序)可以分成以下幾個部分:
- (可選)一個符号字元(’+’ 或 ‘-’)
- 至少一位數字
部分數值列舉如下:
["+100", “5e2”, “-123”, “3.1416”, “-1E-16”, “0123”]
部分非數值列舉如下:
[“12e”, “1a3.14”, “1.2.3”, “±5”, “12e+5.4”]
示例1
輸入:s = "0"
輸出:true
示例2
輸入:s = "e"
輸出:false
示例3
輸入:s = "."
輸出:false
示例4
輸入:s = " .1 "
輸出:true
Leetcode連結:劍指offer面試題20:表示數值的字元串
算法分析
本題采用有限狀态自動機,每次狀态轉移隻輸出true或者false,判斷目前狀态是接受狀态(以該狀态結尾是合法的)還是拒絕狀态(與接受狀态相反)。
- 确定狀态
- 起始的空格
- 符号位
- 整數部分
- 左側有整數的小數點
- 左側無整數的小數點
- 小數部分
- 幂符号 e \text{e} e 或 E \text{E} E
- 指數符号位
- 指數階數
-
末尾的空格
易知,接受狀态為2、3、5、8、9,剩下的狀态為拒絕狀态
- 狀态轉移規則
- 當 c 為空格時,執行 t = ’ ’
- 當 c 為正負号時,執行 t = ‘s’ ;
- 當 c 為數字時,執行 t = ‘d’ ;
- 當 c 為 e \text{e} e, E \text{E} E 時,執行 t = ‘e’ ;
- 當 c 為 . 時,執行 t = ‘.’;
- 否則,執行 t = ‘?’ ,代表為不屬于判斷範圍的非法字元,後續直接傳回 falsefalse 。
- 複雜度分析
- 需要周遊字元串,是以時間複雜度為O(N)
- 額外的空間states與問題規模(字元串長度)無關,是以時間複雜度為O(1)
- 對比使用暴力枚舉的好處
- 減少if else, switch case的使用次數,代碼更加簡潔優雅
- 狀态表示直覺易讀,不易漏掉狀态
- 參考題解
Golang代碼如下
func isNumber(s string) bool {
var states = []map[rune]int{
map[rune]int{ ' ': 0, 's': 1, 'd': 2, '.': 4}, // 0
map[rune]int{ 'd': 2, '.': 4 }, // 1
map[rune]int{ 'd': 2, '.': 3, 'e': 6, ' ': 9 }, // 2
map[rune]int{ 'd': 5, ' ': 9, 'e': 6 }, // 3
map[rune]int{ 'd': 5 }, // 4
map[rune]int{ 'd': 5, 'e': 6, ' ': 9 }, // 5
map[rune]int{ 's': 7, 'd': 8 }, // 6
map[rune]int{ 'd': 8 }, // 7
map[rune]int{ 'd': 8, ' ': 9 }, // 8
map[rune]int{ ' ': 9 }, // 9
}
state := 0
var t rune
for _, v := range s {
if v == ' ' {
t = ' '
} else if v == '+' || v == '-' {
t = 's'
} else if v >= '0' && v <= '9' {
t = 'd'
} else if v == '.' {
t = '.'
} else if v == 'e' || v == 'E' {
t = 'e'
} else {
t = '?'
}
if _, ok := states[state][t]; !ok {
return false
}
state = states[state][t]
}
return state == 2 || state == 3 || state == 5 || state == 8 || state == 9
}