Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
A string is 'beautiful' if it splits into 2+ consecutive increasing integers (each one more than the last) with no leading zeros. Try every possible length L for the first number (from 1 up to n/2). Parse that first value, then keep appending value+1, value+2, … as strings until you've built a string at least as long as s; if the rebuilt string equals s, it's beautiful — output 'YES' and the first number. If no L works, output 'NO'. O(n²) time worst case, O(n) space.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you see that fixing the first number's length fully determines the sequence, and whether you handle leading zeros and integer overflow — the two traps that fail otherwise-correct solutions.
Where candidates slip
Using a 32-bit int and overflowing on long inputs, or forgetting the leading-zero rule (accepting '0102' style strings), or letting the first number span more than half the string.
How to stand out
Explain that reconstruction is more robust than trying to delimit numbers in place, and call out the long/BigInteger and leading-zero guards before the interviewer asks.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previews, n = len(s)s[0]=='0'?YesNoExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2 · Optimal
Enumerate First-Number Length and Reconstruct (Optimal)
1Hint 1Explore hintHide hint
Once you know the first number, every later number is forced (just +1 each time). What's the only thing left to choose?
2Hint 2Explore hintHide hint
Try each possible length L (1 to n/2) for the first number. Parse it, then keep appending the next integers as strings.
3Hint 3Explore hintHide hint
Build until the string is at least as long as s, then compare for exact equality. Handle leading zeros and use a 64-bit integer.
A · Core idea
For each candidate first-number length L from 1 to n/2, treat s[0:L] as the first integer x. Then simulate the sequence: repeatedly increment x and append its decimal form to a buffer that starts as s[0:L], stopping once the buffer's length reaches n. If the buffer exactly equals s, the string is beautiful with first number s[0:L].
Why fixing L determines everything. The sequence must increase by exactly one. So the value of the first number plus where it ends completely specifies every following number — there are no further choices. Enumerating L therefore enumerates every possible beautiful decomposition.
Why reconstruct instead of parse-in-place. Trying to read off the next number's boundary is fiddly: 9 → 10 changes digit count, 99 → 100 again, and so on. Reconstruction sidesteps all of that — generate the canonical concatenation and compare strings. If they match, the boundaries were correct by construction.
Why `L ≤ n/2`. A beautiful string needs at least two numbers. If the first number used more than half the characters, there wouldn't be room for a strictly-longer-or-equal second number, so such L can't yield two parts that fill s.
Why the leading-zero guard. No number may start with 0. Since the first character of s is the first digit of the first number, s[0]=='0' makes every L invalid — short-circuit to NO. Generated numbers (x+1, …) never have leading zeros, so only the first needs checking.
Why 64-bit / big integers. With |s| up to 32 the first number can be ~16 digits, overflowing a 32-bit int. Use long in Java (or Python's unbounded ints) so increments stay correct.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "91011"Array state
L=1
built = "9"
YES 9C · Code with teaching comments
D · Why it works and complexityO(n²) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
This is enumeration over the single degree of freedom. Many 'can this string be decomposed by a rule?' problems collapse once you identify the one unknown that fixes the rest — here, the first number's length. Reconstruct-and-compare turns a parsing puzzle into a string equality check.
ComplexityO(n²) time, O(n) space — Up to n/2 choices of L; for each, reconstruction appends digits totaling O(n) and the comparison is O(n) → O(n²) worst case. The rebuilt buffer is O(n) space. With n ≤ 32 this is trivially fast.
E · Edge cases and pitfalls8s = "99100" — L=2 first=99 → 99,100 → "99100" matches → YES 99.Explore edge casesHide edge cases
Edge cases handled
s = "99100"L=2 first=99 → 99,100 → "99100" matches → YES 99.s = "010203"s[0]=='0' → leading zero → NO immediately.s = "1213141516171819202122"L=2 first=12 → 12..22 → matches → YES 12.s = "101103"No L reconstructs it (10,11,12… ≠ 101103) → NO.
Pitfalls
- 32-bit int overflow on long first numbers — use long / big integers.
- Forgetting the leading-zero check, accepting '0'-prefixed numbers.
- Letting L exceed n/2, allowing a single-number 'split'.
- Parsing boundaries manually instead of reconstructing (error-prone across digit-length jumps like 9→10).
Approach 2 of 2
Greedy Single-Number Boundary Parse (Pitfall Alternative)
1Hint 1Explore hintHide hint
You could try to read the first number, then expect the next number's string right after it.
2Hint 2Explore hintHide hint
But when numbers grow a digit (9→10, 99→100), the expected next length changes — easy to mishandle.
3Hint 3Explore hintHide hint
It's the same O(n²) cost as reconstruction but far more bug-prone; prefer rebuilding and comparing.
A · Core idea
Instead of rebuilding the whole string, this variant fixes the first length L, computes x, then walks forward expecting the digits of x+1, then x+2, and so on — advancing a cursor by the digit-length of each expected number.
Why it's tempting. It avoids building a separate buffer and feels more 'in place'.
Why it's risky. The expected next number's digit length changes at powers of ten (9→10, 99→100). Each transition needs care to slice the right number of characters from s, and an off-by-one silently rejects valid strings or accepts invalid ones. The reconstruction approach folds all of that into one str(x) call and a final equality check, eliminating the boundary bookkeeping entirely.
Verdict. Same asymptotic cost, more failure modes. Use it as a talking point about why reconstruction is the cleaner contract, then implement the reconstruction version.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "99100"Array state
first 99
cursor → index 2
YES 99C · Code with teaching comments
Reference implementation previewpublic String separateNumbers(String s) { int n = s.length(); if (s.charAt(0) == '0') return "NO"; for (int len = 1; len <= n / 2; len++) { long x = Long.parseLong(s.substring(0, len));Explore the full codeHide the full code
D · Why it works and complexityO(n²) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
Comparing against a regenerated canonical form is almost always more robust than manually re-deriving boundaries. Let the standard toString/str define the boundaries for you.
ComplexityO(n²) time, O(1) space — Still tries each first-number length and walks the rest of the string, so O(n²) worst case, O(1) extra beyond the running expected value.
E · Edge cases and pitfalls6s = "99100" — Digit-length jump 2→3 handled by startsWith → YES 99.Explore edge casesHide edge cases
Edge cases handled
s = "99100"Digit-length jump 2→3 handled by startsWith → YES 99.s = "1234"1,2,3,4 each length 1 → YES 1.s = "010203"Leading zero → NO.
Pitfalls
- Mismanaging the cursor when the expected number gains a digit (9→10, 99→100).
- Overrunning the end of s without a bounds check.
- Same overflow and leading-zero traps as the reconstruction version.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic String separateNumbers(String s) { int n = s.length(); if (s.charAt(0) == '0') return "NO"; for (int len = 1; len <= n / 2; len++) { long first = Long.parseLong(s.substring(0, len));Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Integer overflow
With |s| up to 32, the first number can be ~16 digits and overflow a 32-bit int, corrupting all subsequent increments.
Explore the code comparisonHide the code comparison
Integer overflow
With |s| up to 32, the first number can be ~16 digits and overflow a 32-bit int, corrupting all subsequent increments.
Don't write this
int first = Integer.parseInt(s.substring(0, len));Write this instead
long first = Long.parseLong(s.substring(0, len));2Missing the leading-zero rule
No number may start with '0'. If s begins with '0' the answer is NO, but a naive reconstruction of 0,1,2 ('012') would wrongly match some inputs.
Explore the code comparisonHide the code comparison
Missing the leading-zero rule
No number may start with '0'. If s begins with '0' the answer is NO, but a naive reconstruction of 0,1,2 ('012') would wrongly match some inputs.
Don't write this
for length in range(1, n // 2 + 1):
first = int(s[:length]) # silently allows '0...'
...Write this instead
if s[0] == '0':
return "NO"
for length in range(1, n // 2 + 1):
first = int(s[:length])
...3First number too long
A beautiful string needs at least two numbers. Letting L run to n accepts the trivial 'one number' case, which isn't valid.
Explore the code comparisonHide the code comparison
First number too long
A beautiful string needs at least two numbers. Letting L run to n accepts the trivial 'one number' case, which isn't valid.
Don't write this
for length in range(1, n + 1):Write this instead
for length in range(1, n // 2 + 1):Revise
Carry the pattern into the interview
Pattern to remember
Try each first-number length, rebuild and compare
Code pattern to memorize
for L in 1 .. n/2:
if s[0]=='0': break # leading zero
x = int(s[:L]); built = s[:L]
while len(built) < n:
x += 1; built += str(x)
if built == s: return "YES " + str(int(s[:L]))
return "NO"Rules to remember
- 1The first number's length
Lranges only up ton/2— you need at least two numbers. - 2No number may have a leading zero; if
s[0]=='0'the answer is immediately 'NO'. - 3Rebuild by concatenating
x, x+1, x+2, …and compare tos— don't try to parse boundaries directly. - 4Use a 64-bit / big integer: a first number up to 16 digits won't fit in 32 bits.
- 5Stop building as soon as the rebuilt string reaches length
n, then check exact equality.
Use this pattern when
- Decide if a digit string decomposes into a consecutive increasing sequence
- The first chunk's size is unknown but bounded, so enumerate it
- Verification is easier by reconstruction than by parsing
- Leading-zero and overflow edge cases must be handled explicitly
Choose another pattern when
- Arbitrary (non-consecutive) splits allowed → general DP partition, not this rebuild
- Numbers decrease or step by k → adapt the increment but same skeleton
- Need all valid splits, not existence → enumerate and collect
- Huge n with huge first numbers → watch reconstruction cost and overflow
Interview takeaway
When the first piece determines everything that follows, enumerate that first piece. Fixing the first number's length pins the entire sequence, so you just rebuild 'x, x+1, x+2…' and compare. Reconstruction sidesteps tricky boundary parsing, and the leading-zero / overflow guards are the easy points to lose.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternEnumerate the single degree of freedom, then reconstruct-and-compare. When a rule fixes everything after the first piece, iterate over that first piece's length, regenerate the canonical sequence, and test string equality. Reconstruction beats in-place boundary parsing, and overflow / leading-zero guards are the scoring points.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Split into Fibonacci SequenceComing soonSame enumerate-then-verify, but each number is the sum of the previous two.
- Split Array into Fibonacci SequenceComing soonLeetCode version with overflow and leading-zero rules — nearly identical guards.
- Restore IP AddressesComing soonEnumerate segment boundaries with leading-zero and range constraints.
- Additive NumberComing soonDecide if a string is an additive sequence — fix the first two numbers, then verify.