The Generalized Abbreviation problem asks you to generate all possible generalized abbreviations for a given word. An abbreviation replaces any number of consecutive
characters with their count, but you cannot abbreviate non-consecutive letters in a single group. For example, given the word word
, valid abbreviations include word
, w1rd
, 2rd
, 1o1d
, 4
, etc.
word
.12d
is not valid for word
).At first glance, this problem looks like a variation of generating all subsets or permutations, but with the twist that we can abbreviate any run of consecutive characters into a single number. The brute-force way would be to try all possible ways to abbreviate or not abbreviate each character, but we need to ensure that numbers are not repeated or adjacent.
For each character, we have two choices:
As we explore, we build up the abbreviation string. When we reach the end, if there's a non-zero abbreviation count, we append it to the result. This ensures no two numbers are adjacent and every abbreviation is valid.
Let's break down the solution step-by-step:
This approach ensures we generate every valid abbreviation exactly once, and by using recursion, the code remains clean and easy to follow.
Let's walk through an example with the word word
:
w o r d
→ 4
(all four characters abbreviated).w o r d
→ word
.w o r d
→ 2rd
.w o r d
→ 1o2
.w o r d
→ w2d
.At each step, if we abbreviate, we increase the running count; if we keep, we append the count (if any) and the character, then reset.
class Solution:
def generateAbbreviations(self, word: str):
res = []
def backtrack(pos, cur, count):
if pos == len(word):
if count > 0:
cur += str(count)
res.append(cur)
return
# Option 1: Abbreviate this character
backtrack(pos + 1, cur, count + 1)
# Option 2: Keep this character
new_cur = cur
if count > 0:
new_cur += str(count)
new_cur += word[pos]
backtrack(pos + 1, new_cur, 0)
backtrack(0, "", 0)
return res
class Solution {
public:
vector<string> generateAbbreviations(string word) {
vector<string> res;
backtrack(word, 0, "", 0, res);
return res;
}
void backtrack(const string& word, int pos, string cur, int count, vector<string>& res) {
if (pos == word.size()) {
if (count > 0) cur += to_string(count);
res.push_back(cur);
return;
}
// Option 1: Abbreviate this character
backtrack(word, pos + 1, cur, count + 1, res);
// Option 2: Keep this character
string new_cur = cur;
if (count > 0) new_cur += to_string(count);
new_cur += word[pos];
backtrack(word, pos + 1, new_cur, 0, res);
}
};
class Solution {
public List<String> generateAbbreviations(String word) {
List<String> res = new ArrayList<>();
backtrack(word, 0, "", 0, res);
return res;
}
private void backtrack(String word, int pos, String cur, int count, List<String> res) {
if (pos == word.length()) {
if (count > 0) cur += count;
res.add(cur);
return;
}
// Option 1: Abbreviate this character
backtrack(word, pos + 1, cur, count + 1, res);
// Option 2: Keep this character
String newCur = cur;
if (count > 0) newCur += count;
newCur += word.charAt(pos);
backtrack(word, pos + 1, newCur, 0, res);
}
}
var generateAbbreviations = function(word) {
const res = [];
function backtrack(pos, cur, count) {
if (pos === word.length) {
if (count > 0) cur += count;
res.push(cur);
return;
}
// Option 1: Abbreviate this character
backtrack(pos + 1, cur, count + 1);
// Option 2: Keep this character
let newCur = cur;
if (count > 0) newCur += count;
newCur += word[pos];
backtrack(pos + 1, newCur, 0);
}
backtrack(0, "", 0);
return res;
};
n
, there are 2^n
ways to choose which characters to abbreviate or not. Each choice can produce a unique abbreviation. So, the number of abbreviations is O(2^n)
.
2^n
combinations, but we avoid invalid forms (like adjacent numbers) by design. Thus, the time complexity is O(2^n)
for generating all abbreviations, and space complexity is also O(2^n * n)
as we store all output strings (some of which can be up to length n
).
The Generalized Abbreviation problem is a classic example of using backtracking to explore all possible ways to abbreviate a string by replacing consecutive characters with their counts. By making a decision at each character (to abbreviate or not), and carefully handling the abbreviation count, we efficiently generate all valid abbreviations without duplicates or invalid forms. The recursive approach is both elegant and intuitive, and it ensures that all possible abbreviations are covered with clean, readable code.