Missing undo step on backtrack
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.
Move from brute-force thinking to an efficient approach using backtracking strategy.
We had some 2-dimensional coordinates, like "(1, 3)" or "(2, 0.5)". Then, we removed all commas, decimal points, and spaces and ended up with the string s.
"(1, 3)" becomes s = "(13)" and "(2, 0.5)" becomes s = "(205)".Return a list of strings representing all possibilities for what our original coordinates could have been.
Our original representation never had extraneous zeroes, so we never started with numbers like "00", "0.0", "0.00", "1.0", "001", "00.01", or any other number that can be represented with fewer digits. Also, a decimal point within a number never occurs without at least one digit occurring before it, so we never started with numbers like ".1".
The final answer list can be returned in any order. All coordinates in the final answer have exactly one space between them (occurring after the comma.)
Example 1:
Input: s = "(123)" Output: ["(1, 2.3)","(1, 23)","(1.2, 3)","(12, 3)"]
Example 2:
Input: s = "(0123)" Output: ["(0, 1.23)","(0, 12.3)","(0, 123)","(0.1, 2.3)","(0.1, 23)","(0.12, 3)"] Explanation: 0.0, 00, 0001 or 00.01 are not allowed.
Example 3:
Input: s = "(00011)" Output: ["(0, 0.011)","(0.001, 1)"]
Constraints:
4 <= s.length <= 12s[0] == '(' and s[s.length - 1] == ')'.s are digits.Problem summary: We had some 2-dimensional coordinates, like "(1, 3)" or "(2, 0.5)". Then, we removed all commas, decimal points, and spaces and ended up with the string s. For example, "(1, 3)" becomes s = "(13)" and "(2, 0.5)" becomes s = "(205)". Return a list of strings representing all possibilities for what our original coordinates could have been. Our original representation never had extraneous zeroes, so we never started with numbers like "00", "0.0", "0.00", "1.0", "001", "00.01", or any other number that can be represented with fewer digits. Also, a decimal point within a number never occurs without at least one digit occurring before it, so we never started with numbers like ".1". The final answer list can be returned in any order. All coordinates in the final answer have exactly one space between them (occurring after the comma.)
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Backtracking
"(123)"
"(0123)"
"(00011)"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #816: Ambiguous Coordinates
class Solution {
public List<String> ambiguousCoordinates(String s) {
int n = s.length();
List<String> ans = new ArrayList<>();
for (int i = 2; i < n - 1; ++i) {
for (String x : f(s, 1, i)) {
for (String y : f(s, i, n - 1)) {
ans.add(String.format("(%s, %s)", x, y));
}
}
}
return ans;
}
private List<String> f(String s, int i, int j) {
List<String> res = new ArrayList<>();
for (int k = 1; k <= j - i; ++k) {
String l = s.substring(i, i + k);
String r = s.substring(i + k, j);
boolean ok = ("0".equals(l) || !l.startsWith("0")) && !r.endsWith("0");
if (ok) {
res.add(l + (k < j - i ? "." : "") + r);
}
}
return res;
}
}
// Accepted solution for LeetCode #816: Ambiguous Coordinates
func ambiguousCoordinates(s string) []string {
f := func(i, j int) []string {
res := []string{}
for k := 1; k <= j-i; k++ {
l, r := s[i:i+k], s[i+k:j]
ok := (l == "0" || l[0] != '0') && (r == "" || r[len(r)-1] != '0')
if ok {
t := ""
if k < j-i {
t = "."
}
res = append(res, l+t+r)
}
}
return res
}
n := len(s)
ans := []string{}
for i := 2; i < n-1; i++ {
for _, x := range f(1, i) {
for _, y := range f(i, n-1) {
ans = append(ans, "("+x+", "+y+")")
}
}
}
return ans
}
# Accepted solution for LeetCode #816: Ambiguous Coordinates
class Solution:
def ambiguousCoordinates(self, s: str) -> List[str]:
def f(i, j):
res = []
for k in range(1, j - i + 1):
l, r = s[i : i + k], s[i + k : j]
ok = (l == '0' or not l.startswith('0')) and not r.endswith('0')
if ok:
res.append(l + ('.' if k < j - i else '') + r)
return res
n = len(s)
return [
f'({x}, {y})' for i in range(2, n - 1) for x in f(1, i) for y in f(i, n - 1)
]
// Accepted solution for LeetCode #816: Ambiguous Coordinates
struct Solution;
trait Nums {
fn nums(self) -> Vec<String>;
}
impl Nums for &[char] {
fn nums(self) -> Vec<String> {
let n = self.len();
let mut res = vec![];
for i in 1..=n {
let left: String = self[..i].iter().collect();
let right: String = self[i..].iter().collect();
if left.starts_with('0') && left != "0" {
continue;
}
if right.ends_with('0') {
continue;
}
res.push(format!(
"{}{}{}",
left,
if i == n { "" } else { "." },
right
));
}
res
}
}
impl Solution {
fn ambiguous_coordinates(s: String) -> Vec<String> {
let s: Vec<char> = s.chars().collect();
let n = s.len();
let mut res = vec![];
for i in 2..n - 1 {
let left = &s[1..i];
let right = &s[i..n - 1];
for l in left.nums() {
for r in right.nums() {
res.push(format!("({}, {})", l, r));
}
}
}
res
}
}
#[test]
fn test() {
let s = "(123)".to_string();
let mut res = vec_string!["(1, 23)", "(12, 3)", "(1.2, 3)", "(1, 2.3)"];
let mut ans = Solution::ambiguous_coordinates(s);
res.sort();
ans.sort();
assert_eq!(ans, res);
let s = "(00011)".to_string();
let mut res = vec_string!["(0.001, 1)", "(0, 0.011)"];
let mut ans = Solution::ambiguous_coordinates(s);
res.sort();
ans.sort();
assert_eq!(ans, res);
let s = "(0123)".to_string();
let mut res = vec_string![
"(0, 123)",
"(0, 12.3)",
"(0, 1.23)",
"(0.1, 23)",
"(0.1, 2.3)",
"(0.12, 3)"
];
let mut ans = Solution::ambiguous_coordinates(s);
res.sort();
ans.sort();
assert_eq!(ans, res);
let s = "(100)".to_string();
let mut res = vec_string!["(10, 0)"];
let mut ans = Solution::ambiguous_coordinates(s);
res.sort();
ans.sort();
assert_eq!(ans, res);
}
// Accepted solution for LeetCode #816: Ambiguous Coordinates
function ambiguousCoordinates(s: string): string[] {
s = s.slice(1, s.length - 1);
const n = s.length;
const dfs = (s: string) => {
const res: string[] = [];
for (let i = 1; i < s.length; i++) {
const t = `${s.slice(0, i)}.${s.slice(i)}`;
if (`${Number(t)}` === t) {
res.push(t);
}
}
if (`${Number(s)}` === s) {
res.push(s);
}
return res;
};
const ans: string[] = [];
for (let i = 1; i < n; i++) {
for (const left of dfs(s.slice(0, i))) {
for (const right of dfs(s.slice(i))) {
ans.push(`(${left}, ${right})`);
}
}
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.