剑指 Offer(第2版)面试题 20:表示数值的字符串
- 剑指 Offer(第2版)面试题 20:表示数值的字符串
- 解法1:模拟
- 解法2:分段匹配
- 解法 3:DFA
剑指 Offer(第2版)面试题 20:表示数值的字符串
题目来源:31. 表示数值的字符串
解法1:模拟
注意以下几点:
- 小数可以没有整数部分,例如.123等于0.123;
- 小数点后面可以没有数字,例如233.等于233.0;
- 小数点前面和后面可以有数字,例如233.666;
- 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
- 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4。
可能存在字符串前后有空格的情况,下面的函数可以删除空格:
string delSpaceStr(string s)
{int n = s.length(), i = 0, j = n - 1;while (i < n && s[i] == ' ')i++;while (j >= 0 && s[j] == ' ')j--;if (i <= j)return s.substr(i, j - i + 1);return "";
}
代码:
class Solution
{
public:bool isNumber(string s){if (s[0] == '-' || s[0] == '+')s = s.substr(1);if (s.empty() || s[0] == '.' && s.size() == 1)return false;int n = s.length();int point = 0, e = 0;for (int i = 0; i < n; i++){if (isdigit(s[i])){// 如果 s[i] 是数字 '0'~'9',跳过continue;}else if (s[i] == '.') // 如果 s[i] 是小数点{point++;if (e || point > 1) {// 小数点不能超过 1 个,且小数点不能出现在 'e'/'E' 之后return false;}}else if (s[i] == 'e' || s[i] == 'E'){e++;if (e > 1){return false;}if (i == 0 || i == n - 1){// 'e'/'E' 不能出现在数值的开头和末尾return false;}if (i == 1 && s[0] == '.'){// 不能有 '.e' 或 '.E'return false;}if (s[i + 1] == '+' || s[i + 1] == '-'){// 'e'/'E' 后可以接加减号,但是加减号不能是末尾if (i + 2 == n)return false;// 跳过加减号i++;}}else{// 其他字符都是非法的!return false;}}return true;}
};
复杂度分析:
时间复杂度:O(n),其中 n 是字符串 s 的长度。
空间复杂度:O(1)。
解法2:分段匹配
官方解法。
表示数值的字符串遵循模式 A[.[B]][E|eC] 或者 .[B][E|eC],其中 A 是数值的整数部分,B 紧跟小数点,是数值的小数部分,C紧跟着 ‘e’/‘E’,是数值的指数部分。
在数值里可能没有数值的整数部分,因此 A 是可有可无的。如果一个数没有整数部分,那么它必须有小数部分。
另外,A 和 C 都可能以 ‘+’/‘-’ 开头,B 一定不能以 ‘+’/‘-’ 开头。
代码:
class Solution
{
public:bool isNumber(string s){if (s.empty())return false;int idx = 0;// 扫描数值的整数部分bool numeric = scanInteger(s, idx);// 如果出现了 '.',则接下来是数值的小数部分if (s[idx] == '.'){idx++;numeric = scanUnsignedInteger(s, idx) || numeric;}// 如果出现了 'e'/'E',则接下来是数值的指数部分if (s[idx] == 'e' || s[idx] == 'E'){idx++;numeric = scanInteger(s, idx) && numeric;}return numeric && idx == s.length();}bool scanInteger(const string s, int &index){if (s[index] == '+' || s[index] == '-')index++;return scanUnsignedInteger(s, index);}bool scanUnsignedInteger(const string s, int &index){int begin = index;while (index < s.length() && isdigit(s[index]))index++;return index > begin;}
};
复杂度分析:
时间复杂度:O(n),其中 n 是字符串 s 的长度。
空间复杂度:O(1)。
解法 3:DFA
看不懂的炫技解法,和状态机有关。
class Solution {
public:bool isNumber(string s) {if(s.empty()) return false;int n = s.size();int state = 0;vector<bool> finals({0, 0, 0, 1, 0, 1, 1, 0, 1}); // 合法的终止状态vector<vector<int> > transfer({{0, 1, 6, 2, -1, -1},{-1, -1, 6, 2, -1, -1},{-1, -1, 3, -1, -1, -1},{8, -1, 3, -1, 4, -1},{-1, 7, 5, -1, -1, -1},{8, -1, 5, -1, -1, -1},{8, -1, 6, 3, 4, -1},{-1, -1, 5, -1, -1, -1},{8, -1, -1, -1, -1, -1},});for(int i = 0; i < n; ++i) {state = transfer[state][_make(s[i])];if(state < 0) return false;}return finals[state];}private:int _make(const char& c) {switch(c) {case ' ': return 0;case '+': return 1;case '-': return 1;case '.': return 3;case 'e': return 4;case 'E': return 4;default: return _number(c);}}int _number(const char& c) {if(c >= '0' && c <= '9') return 2;else return 5;}
};