剑指Offer-数据结构 20. 表示数值的字符串
Q: 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"-1E-16"、"0123"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。
这种问题在实际开发过程中一般都不会有太大问题,因为有正则表达式来实现,但是正则表达式如何实现的呢?这就是这个题目在实际生产过程中解决的问题。
这个题目有很多种情况,比如+-号只能在首位和幂符号后面,e符号只能处于中间位置......有很多种情况需要判断,比较麻烦,有没有什么其他方法来解决这种情况很多的判断处理呢?
答案是肯定的,那就是大名鼎鼎的“确定有限状态自动机”。算法原理请参考《力扣官方题解》。
用 当前处理到字符串的哪一部分 当作状态的的表述,找出所有状态,然后确定出初始状态和接收状态的集合。
然后定义转移规则,将自动转移过程以图解的方式表示出来:
typedef int bool;
#define TRUE 1
#define FALSE 0
#define NUL '\0'
enum State {
STATE_INITIAL,
STATE_INT_SIGN,
STATE_INTEGER,
STATE_POINT,
STATE_POINT_WITHOUT_INT,
STATE_FRACTION,
STATE_EXP,
STATE_EXP_SIGN,
STATE_EXP_NUMBER,
STATE_END,
STATE_ILLEGAL
};
enum CharType {
CHAR_NUMBER,
CHAR_EXP,
CHAR_POINT,
CHAR_SIGN,
CHAR_SPACE,
CHAR_ILLEGAL
};
enum CharType toCharType(char ch) {
if (ch >= '0' && ch <= '9') {
return CHAR_NUMBER;
} else if (ch == 'e' || ch == 'E') {
return CHAR_EXP;
} else if (ch == '.') {
return CHAR_POINT;
} else if (ch == '+' || ch == '-') {
return CHAR_SIGN;
} else if (ch == ' ') {
return CHAR_SPACE;
} else {
return CHAR_ILLEGAL;
}
}
enum State transfer(enum State state, enum CharType type) {
switch (state) {
case STATE_INITIAL: {
switch (type) {
case CHAR_SPACE:
return STATE_INITIAL;
case CHAR_NUMBER:
return STATE_INTEGER;
case CHAR_POINT:
return STATE_POINT_WITHOUT_INT;
case CHAR_SIGN:
return STATE_INT_SIGN;
default:
return STATE_ILLEGAL;
}
}
case STATE_INT_SIGN: {
switch (type) {
case CHAR_NUMBER:
return STATE_INTEGER;
case CHAR_POINT:
return STATE_POINT_WITHOUT_INT;
default:
return STATE_ILLEGAL;
}
}
case STATE_INTEGER: {
switch (type) {
case CHAR_NUMBER:
return STATE_INTEGER;
case CHAR_EXP:
return STATE_EXP;
case CHAR_POINT:
return STATE_POINT;
case CHAR_SPACE:
return STATE_END;
default:
return STATE_ILLEGAL;
}
}
case STATE_POINT: {
switch (type) {
case CHAR_NUMBER:
return STATE_FRACTION;
case CHAR_EXP:
return STATE_EXP;
case CHAR_SPACE:
return STATE_END;
default:
return STATE_ILLEGAL;
}
}
case STATE_POINT_WITHOUT_INT: {
switch (type) {
case CHAR_NUMBER:
return STATE_FRACTION;
default:
return STATE_ILLEGAL;
}
}
case STATE_FRACTION: {
switch (type) {
case CHAR_NUMBER:
return STATE_FRACTION;
case CHAR_EXP:
return STATE_EXP;
case CHAR_SPACE:
return STATE_END;
default:
return STATE_ILLEGAL;
}
}
case STATE_EXP: {
switch (type) {
case CHAR_NUMBER:
return STATE_EXP_NUMBER;
case CHAR_SIGN:
return STATE_EXP_SIGN;
default:
return STATE_ILLEGAL;
}
}
case STATE_EXP_SIGN: {
switch (type) {
case CHAR_NUMBER:
return STATE_EXP_NUMBER;
default:
return STATE_ILLEGAL;
}
}
case STATE_EXP_NUMBER: {
switch (type) {
case CHAR_NUMBER:
return STATE_EXP_NUMBER;
case CHAR_SPACE:
return STATE_END;
default:
return STATE_ILLEGAL;
}
}
case STATE_END: {
switch (type) {
case CHAR_SPACE:
return STATE_END;
default:
return STATE_ILLEGAL;
}
}
default:
return STATE_ILLEGAL;
}
}
bool isNumber(char* s) {
int len = strlen(s);
enum State state = STATE_INITIAL;
for (int i = 0; i < len; i++) {
enum CharType type = toCharType(s[i]);
enum State nextState = transfer(state, type);
if (nextState == STATE_ILLEGAL) return FALSE;
state = nextState;
}
return state == STATE_INTEGER || state == STATE_POINT || state == STATE_FRACTION || state == STATE_EXP_NUMBER || state == STATE_END;
}
int main() {
printf("isNumber: %d\n", isNumber("3.1415926"));
printf("isNumber: %d\n", isNumber("+3.1415926"));
printf("isNumber: %d\n", isNumber("-3.1415926"));
printf("isNumber: %d\n", isNumber(" 3.1415926 "));
printf("isNumber: %d\n", isNumber("3.14159E+26"));
printf("isNumber: %d\n", isNumber("3.14159E-26"));
printf("isNumber: %d\n", isNumber("-3.14159E+26"));
printf("isNumber: %d\n", isNumber("x3.1415926"));
printf("isNumber: %d\n", isNumber("x3"));
return 0;
}
小结:
这个题目让我们学会使用状态自动机的方式解决逻辑条件很多的问题,这种方式既能够穷尽各种条件和逻辑转换,又能很好的进行代码维护和扩展,是实际开发中很好的一种算法思想,也特别适合底层的嵌入式开发,也可能常见于面试中。