1. 程式人生 > 實用技巧 >字串轉整數(Python and C++解法)——狀態機的學習和使用

字串轉整數(Python and C++解法)——狀態機的學習和使用

題目:

請你來實現一個atoi函式,使其能將字串轉換成整數。

首先,該函式會根據需要丟棄無用的開頭空格字元,直到尋找到第一個非空格的字元為止。接下來的轉化規則如下:

如果第一個非空字元為正或者負號時,則將該符號與之後面儘可能多的連續數字字元組合起來,形成一個有符號整數。
假如第一個非空字元是數字,則直接將其與之後連續的數字字元組合起來,形成一個整數。
該字串在有效的整數部分之後也可能會存在多餘的字元,那麼這些字元可以被忽略,它們對函式不應該造成影響。
注意:假如該字串中的第一個非空格字元不是一個有效整數字符、字串為空或字串僅包含空白字元時,則你的函式不需要進行轉換,即無法進行有效轉換。

在任何情況下,若函式不能進行有效的轉換時,請返回 0 。

提示:

本題中的空白字元只包括空格字元 ' ' 。
假設我們的環境只能儲存 32 位大小的有符號整數,那麼其數值範圍為[−2^31, 2^31− 1]。如果數值超過這個範圍,請返回 INT_MAX (2^31− 1) 或INT_MIN (−2^31) 。

示例1:輸入: "42" 輸出: 42

示例2:輸入: " -42" 輸出: -42 解釋: 第一個非空白字元為 '-', 它是一個負號。我們儘可能將負號與後面所有連續出現的數字組合起來,最後得到 -42 。

示例3:輸入: "4193 with words" 輸出: 4193 解釋: 轉換截止於數字 '3' ,因為它的下一個字元不為數字。

示例4:輸入: "words and 987" 輸出: 0 解釋: 第一個非空字元是 'w', 但它不是數字或正、負號。因此無法執行有效的轉換。

示例5:輸入: "-91283472332" 輸出: -2147483648 解釋: 數字 "-91283472332" 超過 32 位有符號整數範圍。因此返回 INT_MIN (−231) 。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/string-to-integer-atoi

思路:

  如果採用常規解法,根據轉換規則會寫出很多的判斷條件,程式碼臃腫,容易出錯。

  當涉及字串的轉換時,可以考慮使用狀態機的方法。有限狀態機

(英語:finite-state machine,縮寫:FSM)又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型。有限狀態機可以使用下圖那樣的狀態轉移圖來表示:當前狀態(B)和條件(Y)的組合指示出下一個狀態(C)。

條件X 條件Y 條件Z
狀態A
狀態B 狀態C
狀態C

  首先根據轉換條件畫出狀態機,然後根據狀態機畫出狀態轉移表。

' ' +/- number other
start start signed digital end
signed end end digital end
digital end end digital end
end end end end end

接下來程式設計部分就只需要把上面這個狀態轉換表抄進程式碼即可。

時間複雜度:O(n),其中 n 為字串的長度。我們只需要依次處理所有的字元,處理每個字元需要的時間為 O(1)。

空間複雜度:O(1),自動機的狀態只需要常數空間儲存。

Python解法:

 1 class Automaton:
 2     def __init__(self):
 3         self.state = 'start'  # 初始狀態
 4         self.sign = 1  # 假設資料符號為正,需要乘以1
 5         self.ans = 0
 6         self.table = {  # 狀態轉移表
 7             "start": ["start", "signed", "digital", "end"],
 8             "signed": ["end", "end", "digital", "end"],
 9             "digital": ["end", "end", "digital", "end"],
10             "end": ["end", "end", "end", "end"]
11         }
12     def getColNum(self, c):  # 得到每個條件對應的列序號
13         if c.isspace(): return 0
14         if c == '+' or c == '-':    return 1    
15         if c.isdigit(): return 2
16         return 3
17     def calChar(self, c):
18         self.state = self.table[self.state][self.getColNum(c)]  # 更新狀態
19         if self.state == "digital":
20             self.ans = self.ans * 10 + int(c)
21             if self.sign == 1:  self.ans = min(self.ans, 2**31 - 1)
22             else: self.ans = min(self.ans, 2**31)
23         elif self.state == "signed":  # 更新符號
24             if c == '+':    self.sign = 1
25             else:   self.sign = -1
26 
27 class Solution:
28     def myAtoi(self, str: str) -> int:
29         automaton = Automaton()
30         for c in str:
31             automaton.calChar(c)
32         return automaton.ans * automaton.sign

C++解法:

 1 class Automaton {
 2     string state = "start";  // 初始狀態
 3     unordered_map<string, vector<string>> table = {  // 將狀態轉移表中的狀態記錄下來
 4         {"start", {"start", "signed", "digital", "end"}},
 5         {"signed", {"end", "end", "digital", "end"}},
 6         {"digital", {"end", "end", "digital", "end"}},
 7         {"end", {"end", "end", "end", "end"}}
 8     };
 9     int getColNum(char c) {  // 得到每個條件對應的列序號
10         if(isspace(c))  return 0;  // C庫函式isspace()檢查字元是否是空,如果為空,返回true
11         if(c == '+' || c == '-')    return 1;
12         if(isdigit(c))  return 2;
13         else    return 3;
14     }
15 public:
16     int sign = 1;  // 假設為正數,需要乘以1
17     long long ans = 0;
18     void calChar(char c) {
19         state = table[state][getColNum(c)];  // 更新狀態
20         if(state == "digital") {
21             ans = ans * 10 + c - '0';
22             if(sign == 1)   ans = min(ans, (long long)INT_MAX);
23             else    ans = min(ans, -(long long)INT_MIN);
24         }
25         else if(state == "signed")  // 更新符號
26             if(c == '+')    sign = 1;
27             else    sign = -1;
28     }
29 };
30 
31 class Solution {
32 public:
33     int myAtoi(string str) {
34         Automaton automaton;
35         for(char c : str)
36             automaton.calChar(c);
37         return automaton.sign * automaton.ans;
38     }
39 };