Java logically simple, flexible and clear solution including rules of a valid number


  • 24
    R

    The idea is to identify the rules of a valid number first, then set boolean variables to mark key characters and judge the validity.

    This solution is logically simple and easy to understand, and moreover, it is flexible to extend to the cases where a string of a valid number can accept any space appears anywhere, or/and the exponent can be a decimal number.

    public boolean isNumber(String s) {
    		/**
    		 * isNumber(s)==true if and only if s=s1 or s1+'e'+s2, where s1, s2
    		 * are valid strings of a number without the char 'e', and s2 is an
    		 * integer.
    		 * 
    		 * 'e' : valid_count=0~1; [boolean hasE]
    		 * 
    		 * Valid chars in a string of a number without 'e':
    		 * 
    		 * ' ' : valid_count=0~n; must appear at two ends
    		 * 
    		 * '+/-' : valid_count=0~1; must be the first non-space valid char;
    		 * [boolean hasFirst]
    		 * 
    		 * '.' : valid_count=0~1; cannot appear after 'e'; [boolean hasDot]
    		 * 
    		 * '0~9' : valid_count=1~n; [boolean hasDigit]
    		 */
    
    		s = s.trim();
    		int n = s.length();
    		if (n == 0)
    			return false;
    
    		boolean hasE, hasFirst, hasDot, hasDigit;
    		hasE = hasFirst = hasDot = hasDigit = false;
    
    		char c;
    		for (int i = 0; i < n; i++) {
    			c = s.charAt(i);
    
    			if (c >= '0' && c <= '9') {
    				hasFirst = hasDigit = true;
    				continue;
    			}
    
    			switch (c) {
    			/*
    			 * case ' ': continue;
    			 */ // extend to accept any space everywhere
    			case 'e':
    				// already has 'e' or no digit before 'e'
    				if (hasE || !hasDigit)
    					return false;
    				hasE = true;
    
    				// reset for the exponential number
    				hasFirst = hasDigit = false;
    				hasDot = true; // the exponent must be an integer, hence
    								// regard as if a dot exists already. Set
    								// hasDot = false extending to accept any
    								// (decimal) number as an exponent.
    				continue;
    			case '+':
    			case '-':
    				if (hasFirst)
    					return false;
    				hasFirst = true;
    				continue;
    			case '.':
    				if (hasDot)
    					return false;
    				hasFirst = hasDot = true;
    				continue;
    			default:
    				return false;
    			}
    		}
    
    		return hasDigit;
    	}

  • 0
    C

    simplest solution ever.


  • 0
    S

    Most elegant solution! Thanks.


  • 0
    D
    case '.':
                    if (hasDot)
                        return false;
                    hasFirst = hasDot = true;
                    hasDigit = false;                           //added
                    continue;
    

    This needs to be added


  • 0
    K

    @debodirno Hello, why this should be added, can you show an example?


  • 0
    R

    @debodirno No. It shouldn't be added.
    Examples (in my understanding):
    . is not a number
    .0 is a number

    1.  is a number
      

    0.0 is a number
    In fact, hasDot and hasDigit are not related at all.


  • 0
    R

    . is not a number
    .0 is a number

    1. is a number
      0.0 is a number

  • 0
    R

    test code:

    try{
    		System.out.println(Double.parseDouble("."));
    		}catch(Exception e){
    			System.out.println(e.toString());
    		}
    		System.out.println(Double.parseDouble(".0"));
    		System.out.println(Double.parseDouble("0."));
    		System.out.println(Double.parseDouble("0.0"));
    

    output:
    java.lang.NumberFormatException: For input string: "."
    0.0
    0.0
    0.0

    Because of the third case, my original code is fine.


  • 0
    R

    @rikimberley Simplest solution!


  • 0
    S

    @rikimberley how about "2e1.3e2"
    I think it is still valid but this definition will output false.
    1.3e2 is a Integer.


  • 0
    R

    @Scarlett_comeup Well, I think this problem is kind of pattern matching by regular expression, like the package java.util.regex. It concerns about the FORMAT of the string, but it is not a problem of "say yes if the string can represent a valid number by human understanding (or by MEANING)".


  • 0

    Here is my alternative solution, instead of scan through the string with different states, I partition the string first into smaller pieces which can decide individually easily.

    public class Solution {
        
        public boolean isSignedIntegerNumeric(String s, int start, int end, boolean allowEmpty) {
            if(start>end)
                return allowEmpty;
            if(s.charAt(start)=='-' || s.charAt(start)=='+')
                start++;
            return isUnsignedIntegerNumeric(s, start, end, allowEmpty);
        }
        
        public boolean isUnsignedIntegerNumeric(String s, int start, int end, boolean allowEmpty) {
            if(start>end)
                return allowEmpty;
            for(int k=start; k<=end; k++)
                if(s.charAt(k)<'0' || s.charAt(k)>'9')
                    return false;
            return true;
        }
        
        public boolean isNumeric(String s, int start, int end) {
            int dotIdx = s.indexOf(".", start);
            if(dotIdx != s.lastIndexOf(".", end))
                return false;
            if(dotIdx==-1) {
                return isSignedIntegerNumeric(s, start, end, false);
            } else {
                if(dotIdx==end)
                    return isSignedIntegerNumeric(s, start, dotIdx-1, false);
                else
                    return isSignedIntegerNumeric(s, start, dotIdx-1, true) && 
                           isUnsignedIntegerNumeric(s, dotIdx+1, end, false);
            }
        }
        
        public boolean isNumber(String s) {
            if(s.isEmpty())
                return false;
            s = s.trim();
            int eIdx = s.indexOf("e");
            if(eIdx != s.lastIndexOf("e"))
                return false;
            if(eIdx==-1) {
                return isNumeric(s, 0, s.length()-1);
            } else {
                return isNumeric(s, 0, eIdx-1) &&
                       isSignedIntegerNumeric(s, eIdx+1, s.length()-1, false);
            }
        }
    }

Log in to reply
 

Looks like your connection to LeetCode Discuss was lost, please wait while we try to reconnect.