Text Justification — Solution in Python, Java & C++
Text Justification solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A hard problem on arrays and strings.
Text Justification is a hard coding problem on arrays, strings and simulation, asked in Amazon, Google, Microsoft and Airbnb interviews.
- Difficulty: Hard
- Topics: Arrays, Strings, Simulation
- Asked at: Amazon, Google, Microsoft, Airbnb, Atlassian, Intuit, Capital One, Databricks, Indeed, LinkedIn
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
You are laying out a paragraph for a fixed-width display. Given the words of the paragraph (in order) and the line width maxWidth, return the lines of the fully justified text — every line exactly maxWidth characters long.
Fill the lines greedily: put as many words on a line as fit when consecutive words are separated by at least one space.
Then pad each line with spaces:
- A line that is not the last line and holds two or more words spreads its spaces between the words as evenly as possible. If the gaps cannot all be equal, the gaps further left get one extra space.
- The last line, and any line holding a single word, is left-justified: words separated by exactly one space, with the remaining spaces added at the end.
A word never contains a space, and no word is longer than maxWidth.
Example 1
Input: words = ["Practice","makes","progress","on","CodeKairo","every","day."], maxWidth = 16
Output: ["Practice makes","progress on","CodeKairo every","day. "]
Explanation: Each full line gets all its spare spaces in its single gap; `day.` is the last line, so it is padded on the right.
Example 2
Input: words = ["a","b","c","d","e"], maxWidth = 3
Output: ["a b","c d","e "]
Example 3
Input: words = ["consistency","beats","talent"], maxWidth = 14
Output: ["consistency ","beats talent "]
Explanation: The first line holds a single word, so it is left-justified even though it is not the last line.
Constraints
1 <= words.length <= 3001 <= words[i].length <= 20words[i] consists of English letters, digits and the symbols . , ! ? ( )1 <= maxWidth <= 100words[i].length <= maxWidth
Hints for Text Justification
Hint 1
Handle the paragraph one line at a time: first decide which words go on the line, then decide how to space them.
Hint 2
Words
i..j-1fit on a line when the sum of their lengths plusj - i - 1single spaces is at mostmaxWidth. Extendjwhile the next word still fits.Hint 3
For a middle line with
ggaps andSspare spaces, every gap getsS / gspaces and the firstS % ggaps get one more. The last line and single-word lines are joined with single spaces and padded on the right.
How to solve Text Justification
The greedy rule fixes which words share a line, so the problem is pure bookkeeping: pack a line, then distribute its spaces with integer division, giving the remainder to the leftmost gaps.
Approach
- Start at word
i = 0. Growjfromiwhilelen + words[j].length + (j - i) <= maxWidth, wherelenis the total letters already on the line andj - icounts the single spaces needed before wordj. - Words
i..j-1form the line. Letgaps = j - i - 1andspaces = maxWidth - len. - If
jreached the end, orgaps == 0, join the words with one space and pad the end with spaces up tomaxWidth. - Otherwise each gap gets
spaces / gapsspaces, and the firstspaces % gapsgaps get one extra. - Append the line, set
i = jand repeat until every word is placed.
Why it works
Greedy packing is what the statement prescribes, so each line's word set is forced. Within a line the spaces must total maxWidth - len; an even split differs by at most one between gaps, and the rule that the left gaps are larger pins down exactly which gaps get the extra space — so the output is unique and the construction produces it.
Complexity
- Time —
O(total characters) - Space —
O(maxWidth) besides the output
Pitfalls
- A single-word line in the middle of the paragraph is left-justified — dividing by zero gaps is the usual crash.
- The last line is left-justified even when it has several words, and it still needs trailing spaces up to
maxWidth. - When counting whether word
jfits, include one space for every word already on the line (j - iof them), notj - i - 1.
Reference solution
Python
from typing import List
def fullJustify(words: List[str], maxWidth: int) -> List[str]:
res = []
n, i = len(words), 0
while i < n:
j, length = i, 0
while j < n and length + len(words[j]) + (j - i) <= maxWidth:
length += len(words[j])
j += 1
gaps = j - i - 1
if j == n or gaps == 0:
line = " ".join(words[i:j])
res.append(line + " " * (maxWidth - len(line)))
else:
spaces = maxWidth - length
base, extra = divmod(spaces, gaps)
parts = []
for g in range(i, j):
parts.append(words[g])
if g < j - 1:
parts.append(" " * (base + (1 if g - i < extra else 0)))
res.append("".join(parts))
i = j
return resJava
public static String[] fullJustify(String[] words, int maxWidth) {
List<String> res = new ArrayList<>();
int n = words.length, i = 0;
while (i < n) {
int j = i, len = 0;
while (j < n && len + words[j].length() + (j - i) <= maxWidth) {
len += words[j].length();
j++;
}
int gaps = j - i - 1;
StringBuilder sb = new StringBuilder();
if (j == n || gaps == 0) {
for (int g = i; g < j; g++) {
if (g > i) sb.append(' ');
sb.append(words[g]);
}
while (sb.length() < maxWidth) sb.append(' ');
} else {
int spaces = maxWidth - len, base = spaces / gaps, extra = spaces % gaps;
for (int g = i; g < j; g++) {
sb.append(words[g]);
if (g < j - 1) {
int cnt = base + (g - i < extra ? 1 : 0);
for (int c = 0; c < cnt; c++) sb.append(' ');
}
}
}
res.add(sb.toString());
i = j;
}
return res.toArray(new String[0]);
}C++
vector<string> fullJustify(vector<string>& words, int maxWidth) {
vector<string> res;
int n = words.size(), i = 0;
while (i < n) {
int j = i, len = 0;
while (j < n && len + (int)words[j].size() + (j - i) <= maxWidth) {
len += words[j].size();
j++;
}
int gaps = j - i - 1;
string line;
if (j == n || gaps == 0) {
for (int g = i; g < j; g++) {
if (g > i) line += ' ';
line += words[g];
}
line += string(maxWidth - (int)line.size(), ' ');
} else {
int spaces = maxWidth - len, base = spaces / gaps, extra = spaces % gaps;
for (int g = i; g < j; g++) {
line += words[g];
if (g < j - 1) line += string(base + (g - i < extra ? 1 : 0), ' ');
}
}
res.push_back(line);
i = j;
}
return res;
}JavaScript
var fullJustify = function(words, maxWidth) {
var res = [];
var n = words.length, i = 0;
while (i < n) {
var j = i, len = 0;
while (j < n && len + words[j].length + (j - i) <= maxWidth) {
len += words[j].length;
j++;
}
var gaps = j - i - 1;
var line = "";
if (j === n || gaps === 0) {
line = words.slice(i, j).join(" ");
while (line.length < maxWidth) line += " ";
} else {
var spaces = maxWidth - len;
var base = Math.floor(spaces / gaps), extra = spaces % gaps;
for (var g = i; g < j; g++) {
line += words[g];
if (g < j - 1) {
var cnt = base + (g - i < extra ? 1 : 0);
for (var c = 0; c < cnt; c++) line += " ";
}
}
}
res.push(line);
i = j;
}
return res;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.