package com.freetymekiyan.algorithms.level.hard; /** * Validate if a given string is numeric. * <p> * Some examples: * "0" => true * " 0.1 " => true * "abc" => false * "1 a" => false * "2e10" => true * Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before * implementing one. * <p> * Update (2015-02-10): * The signature of the C++ function had been updated. If you still see your function signature accepts a const char * * argument, please click the reload button to reset your code definition. * <p> * Company Tags: LinkedIn * Tags: Math, String * Similar Problems: (E) String to Integer (atoi) */ public class ValidNumber { /** * Trim heading and trailing whitespaces. * Use 3 booleans to remember whether dot, exp, and number have shown. * Then check violations. * Situations are: * 1) Number: No violations. Just set hasNum to true. * 2) Dot: Cannot appear after previous dot or 'e'. Then set hasDot to true. * 3) Exp: Cannot appear after previous 'e' or when there is no number yet. * | Then set hasExp to true. IMPORTANT!!! Set hasNum to false. * 4) Signs: Can only appear at first or right after e. Otherwise return false. * 5) All other characters, like whitespace, or abc: Return false. */ public boolean isNumber(String s) { s = s.trim(); // Remove whitespaces first. boolean hasDot = false; boolean hasExp = false; boolean hasNum = false; for (int i = 0; i < s.length(); i++) { if ('0' <= s.charAt(i) && s.charAt(i) <= '9') { // Is digit. hasNum = true; } else if (s.charAt(i) == '.') { // Is dot. if (hasExp || hasDot) { // Cannot appear after exp or dot. return false; } hasDot = true; } else if (s.charAt(i) == 'e') { // Is exp. if (hasExp || !hasNum) { // Cannot appear after exp or before number. return false; } hasNum = false; // NOTE here reset the hasNum flag. Avoid 1e. hasExp = true; } else if (s.charAt(i) == '-' || s.charAt(i) == '+') { // Is sign. if (i != 0 && s.charAt(i - 1) != 'e') { // Must be first or after 'e'. return false; } } else { // All other cases are not allowed. return false; } } return hasNum; } /** * Two Pointers. * Whitespace, +, -, digit, ., e. * First skip whitespaces at the two ends with two pointers i and e. * If out of bound, return false. * Then skip possible leading signs like + or -. * These signs can still appear later after "e", though. * Use three booleans to indicate isDigit, isDot, and isExp. * For each char c from i to e: * | If c is a digit, set isDigit to true. * | Else if c is a dot, check if 'e' or '.' already appeared * | If appeared, return false. '.' cannot appear after 'e' or previous '.'. * | Else set isDot to true, means dot appeared. * | Else if c is an 'e': * | If 'e' already appeared or there is no number before 'e', return false. * | Else set isExp to true. And set isDigit to false. * | Else if c is '+' or '-', check if it is after e. * | If previous char is not 'e', return false. * | All other cases, return false. * | Move i forward. * After the check return whether it's a number. */ public boolean isNumberB(String s) { int len = s.length(); int i = 0, e = len - 1; // Skip whitespaces while (i <= e && Character.isWhitespace(s.charAt(i))) { i++; } if (i > len - 1) { return false; } while (e >= i && Character.isWhitespace(s.charAt(e))) { e--; } // Skip leading +/-. if (s.charAt(i) == '+' || s.charAt(i) == '-') { i++; } // Check remain. boolean hasNum = false; // is a digit boolean hasDot = false; // is a '.' boolean hasExp = false; // is a 'e' while (i <= e) { char c = s.charAt(i); if (Character.isDigit(c)) { hasNum = true; } else if (c == '.') { // '.' appear if (hasExp || hasDot) { return false; // exp can't have '.' or dots } hasDot = true; } else if (c == 'e') { // e appear if (hasExp || hasNum == false) { return false; // already e but not num } hasExp = true; // is exp, see whether is num from now hasNum = false; } else if (c == '+' || c == '-') { // +, - must appear after e if (s.charAt(i - 1) != 'e') { return false; } } else { return false; } i++; } return hasNum; // whether is num or not } /** * Trim and convert trimmed string to char array * Deal with sign * Deal with point * Deal with digit after point * Deal with no point or e * Deal with digit after e * Deal with sign, no digit and other character cases */ public boolean isNumberC(String s) { if (s == null || s.length() == 0) { return false; } char[] c = s.trim().toCharArray(); if (c.length == 0) { return false; // all whitespaces } int i = 0; int num = 0; if (c[0] == '+' || c[0] == '-') { i++; // skip sign } for (; i < c.length && (c[i] >= '0' && c[i] <= '9'); i++) { num++; } if (i < c.length && c[i] == '.') { i++; // skip point } for (; i < c.length && (c[i] >= '0' && c[i] <= '9'); i++) { num++; // ! } if (num == 0) { return false; // no digit before or after point } if (i == c.length) { return true; // no point or e } else if (i < c.length && c[i] != 'e') { return false; // last letter not e } else { i++; // skip e } num = 0; // reset num and check numbers after e if (i < c.length && (c[i] == '+' || c[i] == '-')) { i++; } for (; i < c.length && (c[i] >= '0' && c[i] <= '9'); i++) { num++; } if (num == 0) { return false; // no more numbers after e } if (i == c.length) { return true; // no other letter except e } else { return false; // other letter shows up } } }