class Solution:
def maxUniqueSplit(self, s: str) -> int:
def backtrack(start, seen):
if start == len(s):
return 0
max_count = 0
for end in range(start + 1, len(s) + 1):
substr = s[start:end]
if substr not in seen:
seen.add(substr)
count = 1 + backtrack(end, seen)
max_count = max(max_count, count)
seen.remove(substr)
return max_count
return backtrack(0, set())
class Solution {
public:
int maxUniqueSplit(string s) {
unordered_set<string> seen;
return backtrack(s, 0, seen);
}
private:
int backtrack(const string& s, int start, unordered_set<string>& seen) {
if (start == s.size()) return 0;
int maxCount = 0;
for (int end = start + 1; end <= s.size(); ++end) {
string sub = s.substr(start, end - start);
if (seen.find(sub) == seen.end()) {
seen.insert(sub);
int count = 1 + backtrack(s, end, seen);
maxCount = max(maxCount, count);
seen.erase(sub);
}
}
return maxCount;
}
};
import java.util.HashSet;
import java.util.Set;
class Solution {
public int maxUniqueSplit(String s) {
return backtrack(s, 0, new HashSet<>());
}
private int backtrack(String s, int start, Set<String> seen) {
if (start == s.length()) return 0;
int maxCount = 0;
for (int end = start + 1; end <= s.length(); ++end) {
String sub = s.substring(start, end);
if (!seen.contains(sub)) {
seen.add(sub);
int count = 1 + backtrack(s, end, seen);
maxCount = Math.max(maxCount, count);
seen.remove(sub);
}
}
return maxCount;
}
}
var maxUniqueSplit = function(s) {
function backtrack(start, seen) {
if (start === s.length) return 0;
let maxCount = 0;
for (let end = start + 1; end <= s.length; ++end) {
let substr = s.substring(start, end);
if (!seen.has(substr)) {
seen.add(substr);
let count = 1 + backtrack(end, seen);
maxCount = Math.max(maxCount, count);
seen.delete(substr);
}
}
return maxCount;
}
return backtrack(0, new Set());
};
Given a string s
, split it into the maximum number of non-empty substrings such that every substring is unique (no repeats). Return the maximum number of unique substrings you can obtain from such a split.
s
(no characters left unused).
Example:
Input: s = "ababccc"
Output: 5
Explanation: One optimal split is ["a", "b", "ab", "c", "cc"]
.
At first glance, the problem seems to require trying every possible way to split the string and counting how many unique substrings each split gives. But with so many possibilities, brute force would be too slow for longer strings.
The challenge is to ensure that every substring in the split is unique. This means, as we split the string, we need to keep track of what substrings we've already used, and avoid using them again.
A naive way would be to generate all possible splits (which is exponential), but we can use backtracking to explore only valid splits and prune those that reuse substrings. Backtracking lets us try a split, remember which substrings we've used, and undo our choices as we explore all options.
To optimize, we use a set to record substrings we've already used in the current path. This way, we avoid unnecessary work and quickly skip invalid splits.
Let's break down the solution step by step:
set
(hash set) to store substrings that are already part of the current split. This allows O(1) lookup to check if a substring is unique.This approach efficiently explores only valid splits and avoids unnecessary repeated work by leveraging the set for uniqueness checking.
Let's walk through the example s = "ababccc"
step by step:
The answer is 5.
The problem asks for the maximum number of unique substrings you can get by splitting a string. The key insight is to use backtracking to explore all possible splits, while keeping track of which substrings have already been used. By using a set for uniqueness checking and undoing choices as we backtrack, we efficiently search for the optimal solution. The approach is elegant because it leverages recursion and set operations to prune the search space, making it much faster than brute-force for most cases.