剑指 Offer 20. 表示数值的字符串
思路
方法一:常规解法
只检查每个字符后面的字符,需要判断的地方有:
1. 消去字符串的前置空格和后置空格,比如“1 ”是合法的
2. 正负号:
(1) 只能出现在第1个字符,或者出现在e或E的后1个字符;
(2) 正负号的后面一个字符必须是数字, 或者小数点
(3) 最多只有2个正负号
3. 指数e和E:
(1) 只能出现1次
(2) 前面那个字符必须是数字或者小数点,如果是小数点则此小数点不能在开头位置。
比如.e3就是非法的,"46.e3"是合法的
(3) 后面那个字符必须是数字或者正负号
(4) 后面的所有字符都不能出现小数点,这一点可以在小数点的第(3)个判断点再进行判断
4. 小数点:
(1) 只能出现1次
(2) 不能出现在e或E后面的任何位置
(3) 出现在结尾,前面那个字符必须是数字。比如123.是合法的
5. 前置0:可以存在任意个前置0
1 class Solution { 2 private: 3 int ePos = -1; //E或e的位置(下标) 4 int plusMinusSignNum = 0; //正负号的个数 5 int pointPos = -1; //小数点的位置 6 7 public: 8 bool isNumber(string s) { 9 10 /*1. 消去字符串前置空格和后置空格*/ 11 if(s.empty()) 12 return false; 13 14 int start = 0, end = s.length()-1; 15 for(int i = 0; i < s.length(); ++i) { 16 if(s[i] == ' ') 17 start++; 18 else 19 break; 20 } 21 22 if(start == s.length()) 23 return false; 24 25 for(int i = s.length()-1; i >= 0; --i) { 26 if(s[i] == ' ') 27 end--; 28 else 29 break; 30 } 31 32 string s2(end-start+1, ' '); 33 copy(s.begin()+start, s.begin()+end+1, s2.begin()); 34 s = string(s2.begin(), s2.end()); 35 36 for(int i = 0; i < s.length(); ++i) { 37 if(s[i] == '+' || s[i] == '-') { 38 /*2. 检查正负号*/ 39 //(1)只能出现在第1个字符,或者出现在e或E的后1个字符; 40 if(i != ePos+1) 41 return false; 42 //(2)正负号的后面一个字符必须是数字, 或者小数点 43 if(i+1 <= s.length()-1) { 44 if(!( (s[i+1] >= '0' && s[i+1] <= '9') || s[i+1] == '.' )) 45 return false; 46 } else { 47 //后面无字符,直接返回false 48 return false; 49 } 50 51 plusMinusSignNum++; 52 //(3)最多只有2个正负号 53 if(plusMinusSignNum > 2) 54 return false; 55 56 } else if(s[i] == 'e' || s[i] == 'E') { 57 /*3. 检查E或e*/ 58 //(1)只能出现1次 59 if(ePos != -1) 60 return false; 61 62 ePos = i; 63 64 if(i-1 >= 0) { 65 //(2)前面那个字符必须是数字或者小数点,如果是小数点则此小数点不能在开头位置 66 if(!( ( s[i-1] >= '0' && s[i-1] <= '9') || (s[i-1] == '.' && i-1 != 0)) ) 67 return false; 68 } else { 69 //前面无字符,直接返回false 70 return false; 71 } 72 73 //(3)后面那个字符必须是数字或者正负号 74 if(i+1 <= s.length()-1) { 75 if(!( (s[i+1] >= '0' && s[i+1] <= '9') || s[i+1] == '+' || s[i+1] == '-' )) 76 return false; 77 } else { 78 //后面无字符,直接返回false 79 return false; 80 } 81 82 } else if(s[i] == '.') { 83 /*4. 检查小数点*/ 84 85 //(1)只能出现1次 86 if(pointPos != -1) 87 return false; 88 89 pointPos = i; 90 91 //(2)不能出现在e或E后面的任何位置 92 if(pointPos > ePos && ePos != -1) 93 return false; 94 95 //(3)出现在结尾,前面那个字符必须是数字 96 if(i == s.length()-1) { 97 if(i-1 >= 0) { 98 if(s[i-1] < '0' || s[i-1] > '9') 99 return false; 100 } else { 101 return false; 102 } 103 104 } 105 106 } else { 107 //其他符号中,如果有非数字符号 108 if(s[i] < '0' || s[i] > '9') 109 return false; 110 } 111 } 112 113 return true; 114 115 } 116 };
方法二:有限状态自动机
本解法来自:面试题20. 表示数值的字符串(有限状态自动机,清晰图解)
C++实现
1 class Solution { 2 private: 3 /*有限状态自动机DFA*/ 4 unordered_map<char, int> states[9] = { 5 unordered_map<char, int>({{' ', 0}, {'s', 1}, {'d', 2}, {'.', 4}}), // 0. 6 unordered_map<char, int>({{'d', 2}, {'.', 4}}), // 1. 7 unordered_map<char, int>({{'d', 2}, {'.', 3}, {'e', 5}, {' ', 8}}), // 2. 8 unordered_map<char, int>({{'d', 3}, {'e', 5}, {' ', 8}}), // 3. 9 unordered_map<char, int>({{'d', 3}}), // 4. 10 unordered_map<char, int>({{'s', 6}, {'d', 7}}), // 5. 11 unordered_map<char, int>({{'d', 7}}), // 6. 12 unordered_map<char, int>({{'d', 7}, {' ', 8}}), // 7. 13 unordered_map<char, int>({{' ', 8}}) // 8. 14 }; 15 16 public: 17 bool isNumber(string s) { 18 int p = 0; 19 char t; 20 for(char c : s) { 21 if(c >= '0' && c <= '9') t = 'd'; 22 else if(c == '+' || c == '-') t = 's'; 23 else if(c == 'e' || c == 'E') t = 'e'; 24 else if(c == '.' || c == ' ') t = c; 25 else t = '?'; 26 if(states[p].count(t) == 0) 27 return false; 28 p = (int)states[p][t]; 29 } 30 return p == 2 || p == 3 || p == 7 || p == 8; 31 } 32 };
Java实现
1 class Solution { 2 public boolean isNumber(String s) { 3 Map[] states = { 4 new HashMap<>() {{ put(' ', 0); put('s', 1); put('d', 2); put('.', 4); }}, // 0. 5 new HashMap<>() {{ put('d', 2); put('.', 4); }}, // 1. 6 new HashMap<>() {{ put('d', 2); put('.', 3); put('e', 5); put(' ', 8); }}, // 2. 7 new HashMap<>() {{ put('d', 3); put('e', 5); put(' ', 8); }}, // 3. 8 new HashMap<>() {{ put('d', 3); }}, // 4. 9 new HashMap<>() {{ put('s', 6); put('d', 7); }}, // 5. 10 new HashMap<>() {{ put('d', 7); }}, // 6. 11 new HashMap<>() {{ put('d', 7); put(' ', 8); }}, // 7. 12 new HashMap<>() {{ put(' ', 8); }} // 8. 13 }; 14 int p = 0; 15 char t; 16 for(char c : s.toCharArray()) { 17 if(c >= '0' && c <= '9') t = 'd'; 18 else if(c == '+' || c == '-') t = 's'; 19 else if(c == 'e' || c == 'E') t = 'e'; 20 else if(c == '.' || c == ' ') t = c; 21 else t = '?'; 22 if(!states[p].containsKey(t)) return false; 23 p = (int)states[p].get(t); 24 } 25 return p == 2 || p == 3 || p == 7 || p == 8; 26 } 27 }