The Text Justification problem asks you to arrange a list of words into lines of text, so that each line has exactly maxWidth
characters. Words should be packed greedily, meaning as many as possible in a line, with at least one space between words. Extra spaces should be distributed as evenly as possible between the words; if the number of spaces does not divide evenly, the leftmost slots get more spaces. The last line should be left-justified, and no extra space is inserted between words.
maxWidth
characters.
To solve this problem, start by thinking about how you naturally write text: you fill a line with as many words as will fit, then move to the next line. The challenge here is to distribute spaces so each line is exactly maxWidth
characters, and to handle the last line differently (left-justified).
A brute-force solution could try every possible way to split the words into lines and pick the one that fits the constraints. However, this is inefficient and unnecessary, since the problem allows us to pack greedily: always fill the next line with as many words as possible.
The main complexity comes from distributing spaces: for lines with more than one word, you must calculate how many spaces go between each word and ensure the total length matches maxWidth
. For the last line or lines with only one word, you left-justify.
Here’s how to approach the problem step by step:
maxWidth
.maxWidth
.maxWidth - sum(length of words)
.This approach ensures that each line is filled greedily and justified according to the rules. We use simple arrays and string operations, making the algorithm efficient and easy to understand.
Consider words = ["This", "is", "an", "example", "of", "text", "justification."]
and maxWidth = 16
.
maxWidth
, so stop at "an".
"This is an"
"example of text"
"justification. "
Final output:
Brute-force approach: Would involve trying all possible ways to split words into lines, which is exponential in the number of words (O(2^n)), and not feasible.
Optimized greedy approach (our solution):
maxWidth
.The space for the result is proportional to the input size, and we use only a few extra variables.
The Text Justification problem is a classic string formatting challenge. By grouping words greedily and carefully distributing spaces, we can efficiently build justified text lines that meet all constraints. The key insight is to handle space distribution precisely and treat the last line as a special case. This approach is both efficient and elegant, making use of simple data structures and clear logic.
class Solution:
def fullJustify(self, words, maxWidth):
res = []
curr = []
num_of_letters = 0
for word in words:
if num_of_letters + len(word) + len(curr) > maxWidth:
for i in range(maxWidth - num_of_letters):
curr[i % (len(curr)-1 or 1)] += ' '
res.append(''.join(curr))
curr, num_of_letters = [], 0
curr.append(word)
num_of_letters += len(word)
res.append(' '.join(curr).ljust(maxWidth))
return res
class Solution {
public:
vector<string> fullJustify(vector<string>& words, int maxWidth) {
vector<string> res, curr;
int num_of_letters = 0;
for (auto& word : words) {
if (num_of_letters + word.size() + curr.size() > maxWidth) {
for (int i = 0; i < maxWidth - num_of_letters; ++i)
curr[i % (curr.size() - 1 ? curr.size()-1 : 1)] += " ";
string line;
for (auto& w : curr) line += w;
res.push_back(line);
curr.clear();
num_of_letters = 0;
}
curr.push_back(word);
num_of_letters += word.size();
}
string last = "";
for (int i = 0; i < curr.size(); ++i) {
last += curr[i];
if (i != curr.size()-1) last += " ";
}
last += string(maxWidth - last.size(), ' ');
res.push_back(last);
return res;
}
};
class Solution {
public List<String> fullJustify(String[] words, int maxWidth) {
List<String> res = new ArrayList<>();
List<String> curr = new ArrayList<>();
int num_of_letters = 0;
for (String word : words) {
if (num_of_letters + word.length() + curr.size() > maxWidth) {
for (int i = 0; i < maxWidth - num_of_letters; ++i) {
curr.set(i % (curr.size() - 1 == 0 ? 1 : curr.size() - 1), curr.get(i % (curr.size() - 1 == 0 ? 1 : curr.size() - 1)) + " ");
}
StringBuilder line = new StringBuilder();
for (String w : curr) line.append(w);
res.add(line.toString());
curr = new ArrayList<>();
num_of_letters = 0;
}
curr.add(word);
num_of_letters += word.length();
}
StringBuilder last = new StringBuilder();
for (int i = 0; i < curr.size(); ++i) {
last.append(curr.get(i));
if (i != curr.size()-1) last.append(" ");
}
while (last.length() < maxWidth) last.append(" ");
res.add(last.toString());
return res;
}
}
var fullJustify = function(words, maxWidth) {
let res = [];
let curr = [];
let num_of_letters = 0;
for (let word of words) {
if (num_of_letters + word.length + curr.length > maxWidth) {
for (let i = 0; i < maxWidth - num_of_letters; ++i) {
curr[i % (curr.length - 1 || 1)] += ' ';
}
res.push(curr.join(''));
curr = [];
num_of_letters = 0;
}
curr.push(word);
num_of_letters += word.length;
}
let last = curr.join(' ');
last += ' '.repeat(maxWidth - last.length);
res.push(last);
return res;
};