Round numeric string values
Company: Pinterest
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates parsing and numeric manipulation skills, specifically implementing rounding rules for numeric strings and handling decimals, significant digits, and precision edge cases.
Constraints
- 1 <= len(s) <= 100000
- s matches the regex ^[+-]?\d+(\.\d+)?$ (no exponent, no separators)
- mode is either "integer" or "last_sig_digit"
- Rounding rule for mode="integer": nearest integer, ties at .5 round away from zero
- Rounding rule for mode="last_sig_digit": half-up on the last significant digit; if only one significant digit exists, return canonical s
- Output formatting: remove leading zeros (keep one before '.'), remove trailing zeros after '.', remove trailing '.', never return "-0"
Examples
Input:
Expected Output: 4
Input:
Expected Output: 100
Input:
Expected Output: -3
Input:
Expected Output: 125000
Input:
Expected Output: 12
Input:
Expected Output: 0.006
Input:
Expected Output: 10
Input:
Expected Output: 12.5
Solution
def round_numeric_string(s: str, mode: str) -> str:
s = s.strip()
if not s:
return "0"
# Parse sign
sign = 1
if s[0] == '+':
s = s[1:]
elif s[0] == '-':
sign = -1
s = s[1:]
# Split into integer and fractional parts
if '.' in s:
a, b = s.split('.', 1)
else:
a, b = s, ""
if a == "":
a = "0" # ensure at least one digit before decimal for indexing
# Build digits and decimal point index
digits = [ord(c) - 48 for c in (a + b)]
point = len(a)
def format_result(digs, p):
left = ''.join(str(d) for d in digs[:p]).lstrip('0')
if left == "":
left = "0"
right = ''.join(str(d) for d in digs[p:]).rstrip('0')
if right:
return left + "." + right
else:
return left
def apply_sign(res: str) -> str:
if res == "0":
return "0"
return ("-" + res) if sign < 0 else res
def add_one(digs):
k = len(digs) - 1
while k >= 0:
v = digs[k] + 1
if v < 10:
digs[k] = v
return digs
digs[k] = 0
k -= 1
return [1] + digs
if mode == "integer":
# No fractional part
if point >= len(digits):
res = ''.join(str(d) for d in digits[:point]).lstrip('0') or "0"
return apply_sign(res)
# Fractional present
round_up = digits[point] >= 5 # ties and larger go up (away from zero)
int_digs = digits[:point]
if not int_digs:
int_digs = [0]
if round_up:
int_digs = add_one(int_digs[:])
res = ''.join(str(d) for d in int_digs).lstrip('0') or "0"
return apply_sign(res)
elif mode == "last_sig_digit":
# Find first and last non-zero digit
i = None
for idx, d in enumerate(digits):
if d != 0:
i = idx
break
if i is None:
return "0"
j = None
for idx in range(len(digits) - 1, -1, -1):
if digits[idx] != 0:
j = idx
break
# Only one significant digit: return canonical form
if i == j:
res = format_result(digits, point)
return apply_sign(res)
# Reduce precision by one significant digit
last_keep = j - 1
if digits[j] >= 5:
k = last_keep
carry = 1
while k >= 0 and carry:
nv = digits[k] + carry
if nv < 10:
digits[k] = nv
carry = 0
else:
digits[k] = 0
k -= 1
if carry:
digits.insert(0, 1)
point += 1
last_keep += 1
for idx in range(last_keep + 1, len(digits)):
digits[idx] = 0
res = format_result(digits, point)
return apply_sign(res)
else:
# Fallback to integer rounding if mode is invalid
round_up = False
if point < len(digits):
round_up = digits[point] >= 5
int_digs = digits[:point] if point > 0 else [0]
if round_up:
if not int_digs:
int_digs = [0]
k = len(int_digs) - 1
carry = 1
while k >= 0 and carry:
nv = int_digs[k] + carry
if nv < 10:
int_digs[k] = nv
carry = 0
else:
int_digs[k] = 0
k -= 1
if carry:
int_digs = [1] + int_digs
res = ''.join(str(d) for d in int_digs).lstrip('0') or "0"
if res == "0":
return "0"
return ("-" + res) if sign < 0 else res
Explanation
Time complexity: O(n). Space complexity: O(n).
Hints
- Avoid floating-point; work directly on the string digits.
- For integer rounding, comparing only the first digit after the decimal to 5 suffices to decide <0.5 vs >=0.5.
- Represent the number as a digit array plus the decimal index. For last_sig_digit, find the first and last non-zero positions.
- When reducing precision by one significant digit, round the digit before the last non-zero and propagate carry left; then zero-out all less significant digits.
- Normalize the final string: strip leading zeros in the integer part (keep one zero), strip trailing zeros in the fractional part, and omit the decimal point if the fractional part becomes empty.