
Problem Statement
Given an array of strings named words
and an integer maxWidth
, the task is to format the text such that each line of output text has exactly maxWidth
characters while being fully justified on both left and right margins. A text justification involves packing words into lines such that:
- You strive to fit as many words on a line as possible using a greedy approach.
- If a line is not the last line and is not made up of a single word, you pad spaces between words to ensure that each line is
maxWidth
characters long, distributing extra spaces as evenly as possible between words. If spaces can't be distributed equally, then the extra spaces are placed on the left-most gaps. - The last line of the text should be left-justified with no extra space inserted between the words, and spaces are only added after the last word till it reaches
maxWidth
. - Each line of the output should contain the words separated by at least a single space and should neither contain trailing nor leading spaces.
Examples
Example 1
Input:
words = ["This", "is", "an", "example", "of", "text", "justification."], maxWidth = 16
Output:
[ "This is an", "example of text", "justification. " ]
Example 2
Input:
words = ["What","must","be","acknowledgment","shall","be"], maxWidth = 16
Output:
[ "What must be", "acknowledgment ", "shall be " ]
Explanation:
Note that the last line is "shall be " instead of "shall be", because the last line must be left-justified instead of fully-justified. Note that the second line is also left-justified because it contains only one word.
Example 3
Input:
words = ["Science","is","what","we","understand","well","enough","to","explain","to","a","computer.","Art","is","everything","else","we","do"], maxWidth = 20
Output:
[ "Science is what we", "understand well", "enough to explain to", "a computer. Art is", "everything else we", "do " ]
Constraints
1 <= words.length <= 300
1 <= words[i].length <= 20
words[i]
consists of only English letters and symbols.1 <= maxWidth <= 100
words[i].length <= maxWidth
Approach and Intuition
To develop a solution for text justification as per the given requirements, the following approach can be used, inspired by the examples and constraints:
Initialization:
- Start by creating an empty list called
justified_text
to store the result. - Initialize pointers or counters like
current_length
to track the length of the words in the current line including the minimal spaces (one space between each word). - Initialize a temporary list
current_line
to hold the words for the current line.
- Start by creating an empty list called
Building Lines:
- Loop through each word in the
words
array. - For each word, determine if it can be added to the
current_line
without exceeding themaxWidth
when combined with the minimal required spaces. - If it fits, add the word to
current_line
and adjustcurrent_length
. - If it doesn't fit, move to the step of justifying the current line.
- Loop through each word in the
Justifying a Line:
- If the
current_line
is not the last line:- Calculate the total number of spaces needed to reach
maxWidth
. - Distribute these spaces between the words in
current_line
. If the spaces don't distribute evenly, place the extra spaces in the gaps starting from the left.
- Calculate the total number of spaces needed to reach
- If the
current_line
is the last line:- Join the words with a single space and then pad additional spaces at the end until the line reaches
maxWidth
.
- Join the words with a single space and then pad additional spaces at the end until the line reaches
- If the
Adding to Result:
- Once the line is justified, add it to the
justified_text
. - Reset
current_line
andcurrent_length
to start forming the next line.
- Once the line is justified, add it to the
Handling Edge Cases:
- Single word lines: If a line consists of a single word, it should be followed by spaces until the line length reaches
maxWidth
. - Handle the case where all words are processed but the last line has not been added to the result due to the loop termination conditions.
- Single word lines: If a line consists of a single word, it should be followed by spaces until the line length reaches
Following these steps will ensure that each line adheres to the maxWidth
constraint while also respecting the justification rules provided in the problem statement. Checking the examples, it reassures that the approach of padding and spacing distribution is appropriately planned to meet both the functionality and constraint requirements.
Solutions
- C++
- Java
- C
- JavaScript
- Python
class TextFormatter {
public:
vector<string> justifyText(vector<string>& words, int maxWidth) {
vector<string> formattedText;
int index = 0;
while (index < words.size()) {
vector<string> lineWords = gatherWords(index, words, maxWidth);
index += lineWords.size();
formattedText.push_back(buildLine(lineWords, index, words, maxWidth));
}
return formattedText;
}
private:
vector<string> gatherWords(int idx, vector<string>& words, int maxWidth) {
vector<string> line;
int lineLength = 0;
while (idx < words.size() && lineLength + words[idx].size() <= maxWidth) {
line.push_back(words[idx]);
lineLength += words[idx].size() + 1;
idx++;
}
return line;
}
string buildLine(vector<string>& lineWords, int idx, vector<string>& words, int maxWidth) {
int totalLength = -1;
for (const string& word : lineWords) {
totalLength += word.size() + 1;
}
int spaceLeft = maxWidth - totalLength;
if (lineWords.size() == 1 || idx == words.size()) {
string result = joinString(lineWords, " ");
result += string(spaceLeft, ' ');
return result;
}
int spaceCount = lineWords.size() - 1;
int spacesPerWord = spaceLeft / spaceCount;
int additionalSpaces = spaceLeft % spaceCount;
for (int j = 0; j < additionalSpaces; j++) {
lineWords[j] += " ";
}
for (int j = 0; j < spaceCount; j++) {
lineWords[j] += string(spacesPerWord, ' ');
}
return joinString(lineWords, " ");
}
string joinString(vector<string>& lineWords, const string& delimiter) {
if (lineWords.empty()) {
return "";
}
string result(lineWords[0]);
for (int i = 1; i < lineWords.size(); i++) {
result += delimiter + lineWords[i];
}
return result;
}
};
Implement text justification using C++ to format words into a justified text with a specified maximum width. To achieve this:
- Create a
TextFormatter
class containing methods to justify text and manage internals of building and formatting lines. - Use the
justifyText
method within theTextFormatter
class. It initializes storing results and iterates through words to gather and build lines until all words are processed. - Employ two helper methods:
gatherWords
to collect words that fit within the maximum width constraint andbuildLine
to format and distribute spaces among collected words fittingly. - In
buildLine
, calculate the total length of words and distribute spaces. If there is only one word or it's the last line, left-justify by adding trailing spaces. Otherwise, distribute spaces evenly between words, adding extra spaces to the first few words if spaces cannot be evenly divided. joinString
consolidates words into a string with spaces or extra spaces if applicable.- Gather formatted lines into a vector of strings representing the justified text, which the
justifyText
method ultimately returns.
Ensure all edge cases, such as single-word lines or the last line of the text, are handled to meet the requirements of text justification as traditionally understood in typesetting.
class TextFormatter {
public List<String> justifyText(String[] words, int maxWidth) {
List<String> result = new ArrayList<>();
int currentIndex = 0;
while (currentIndex < words.length) {
List<String> lineWords = gatherWords(currentIndex, words, maxWidth);
currentIndex += lineWords.size();
result.add(formatLine(lineWords, currentIndex, words, maxWidth));
}
return result;
}
private List<String> gatherWords(int start, String[] words, int maxWidth) {
List<String> line = new ArrayList<>();
int lineLength = 0;
while (start < words.length && lineLength + words[start].length() <= maxWidth) {
line.add(words[start]);
lineLength += words[start].length() + 1;
start++;
}
return line;
}
private String formatLine(
List<String> currentLine,
int currentIndex,
String[] words,
int maxWidth
) {
int lineLength = -1;
for (String word : currentLine) {
lineLength += word.length() + 1;
}
int remainingSpace = maxWidth - lineLength;
if (currentLine.size() == 1 || currentIndex == words.length) {
return String.join(" ", currentLine) + " ".repeat(remainingSpace);
}
int spaceBetweenWords = remainingSpace / (currentLine.size() - 1);
int additionalSpace = remainingSpace % (currentLine.size() - 1);
for (int j = 0; j < additionalSpace; j++) {
currentLine.set(j, currentLine.get(j) + " ");
}
for (int j = 0; j < currentLine.size() - 1; j++) {
currentLine.set(j, currentLine.get(j) + " ".repeat(spaceBetweenWords));
}
return String.join(" ", currentLine);
}
}
The provided Java solution demonstrates a method for performing text justification, ensuring each line of text fits within a specified maximum width while maintaining even spacing between words. The solution is encapsulated in the TextFormatter
class which contains a method justifyText
that takes an array of words and an integer for the maximum width as parameters.
The
justifyText
method iterates over the input words and constructs each line to not exceed the maxWidth, utilizing subordinate methods for gathering words into a line and formatting the line:- During each iteration,
gatherWords
is called to select as many words as can fit into the current line given the maxWidth. formatLine
is subsequently invoked to properly space the words in the line, considering scenarios where the line might contain only a single word or it's the last line of text.
- During each iteration,
gatherWords
achieves its functionality by adding words to the current line until the addition of another word would exceed maxWidth. It ensures a basic space is considered for each word added.formatLine
applies the necessary spacing:- Calculates the total length of words in the line and the remaining space that needs to be filled with white spaces.
- If the line contains a single word or it's the last line, it joins the words with a single space and appends the remaining spaces at the end.
- For other lines, it distributes extra spaces evenly and adds any remainder to the initial words in the list.
By iterating through all words and correctly appending and formatting words for each line, the justifyText
method effectively compiles a list of justified text lines that conform to the specified width. The solution employs string manipulation and list operations to maintain readability and efficiency.
struct line_content {
char** segments;
int count;
};
struct line_content extractSegments(int index, char** phrases, int totalPhrases, int maxWidth) {
struct line_content line;
line.segments = (char**)malloc(totalPhrases * sizeof(char*));
line.count = 0;
int lineLength = 0;
while (index < totalPhrases && lineLength + strlen(phrases[index]) <= maxWidth) {
line.segments[line.count++] = phrases[index];
lineLength += strlen(phrases[index]) + 1;
index++;
}
return line;
}
char* concatenate(struct line_content parcel, char* spacer) {
if (parcel.count == 0) {
return "";
}
int fullLength = strlen(spacer) * (parcel.count - 1);
for (int i = 0; i < parcel.count; i++) {
fullLength += strlen(parcel.segments[i]);
}
char* result = (char*)malloc((fullLength + 1) * sizeof(char));
strcpy(result, parcel.segments[0]);
for (int i = 1; i < parcel.count; i++) {
strcat(result, spacer);
strcat(result, parcel.segments[i]);
}
return result;
}
char* formatLine(struct line_content segment, int currentWord, int totalWords, int maxWidth) {
int baseLen = -1;
for (int i = 0; i < segment.count; i++) {
baseLen += strlen(segment.segments[i]) + 1;
}
int spacesNeeded = maxWidth - baseLen;
if (segment.count == 1 || currentWord == totalWords) {
char* formatted = (char*)malloc((maxWidth + 1) * sizeof(char));
strcpy(formatted, concatenate(segment, " "));
for (int i = strlen(formatted); i < maxWidth; i++) formatted[i] = ' ';
formatted[maxWidth] = '\0';
return formatted;
}
int dividerCount = segment.count - 1;
int spacesBetween = spacesNeeded / dividerCount;
int extraSpacesCount = spacesNeeded % dividerCount;
for (int k = 0; k < extraSpacesCount; k++) {
segment.segments[k] =
realloc(segment.segments[k], (strlen(segment.segments[k]) + 1 + 1) * sizeof(char));
strcat(segment.segments[k], " ");
}
for (int k = 0; k < dividerCount; k++) {
segment.segments[k] =
realloc(segment.segments[k],
(strlen(segment.segments[k]) + 1 + spacesBetween) * sizeof(char));
char spaceBuffer[spacesBetween + 1];
memset(spaceBuffer, ' ', spacesBetween);
spaceBuffer[spacesBetween] = '\0';
strcat(segment.segments[k], spaceBuffer);
}
return concatenate(segment, " ");
}
char** justifyText(char** phrases, int totalPhrases, int maxWidth, int* linesCount) {
char** justifiedText = (char**)malloc(totalPhrases * sizeof(char*));
*linesCount = 0;
int index = 0;
while (index < totalPhrases) {
struct line_content lineSegment = extractSegments(index, phrases, totalPhrases, maxWidth);
index += lineSegment.count;
justifiedText[*linesCount] = formatLine(lineSegment, index, totalPhrases, maxWidth);
(*linesCount)++;
}
return justifiedText;
}
The provided C code handles the text justification problem, where the goal is to distribute words across a specified width with appropriate spacing. The code breaks down the functionality into several functions, each handling specific aspects of the text justification process. Here is a breakdown of the key components:
struct line_content
- This structure holds an array of strings (
segments
) and a count of these segments.
- This structure holds an array of strings (
extractSegments
function- This function processes input phrases and forms a line ensuring it does not exceed the maximum width. It returns a
line_content
structure containing the selected segments and their count.
- This function processes input phrases and forms a line ensuring it does not exceed the maximum width. It returns a
concatenate
function- Given a
line_content
object and a spacer (string used to separate words), it concatenates the segments into a single string, inserting the spacer between each word. This is useful for both constructing a fully justified line and creating the final formatted string.
- Given a
formatLine
function- It formats a line to meet the requirement of text justification. If the line is the last line in the text or contains only one word, spaces are added at the end. Otherwise, spaces are distributed evenly amongst words. This function dynamically adjusts memory allocation for each segment to accommodate extra spaces.
justifyText
function- This is the main function that orchestrates the text justification by iterating over the input phrases. It uses
extractSegments
to get line segments andformatLine
to format these segments. It tracks the number of lines generated and stores each justified line.
- This is the main function that orchestrates the text justification by iterating over the input phrases. It uses
Usage Steps
- Define the maximum width (
maxWidth
), an array of strings (phrases
), and its size (totalPhrases
). - Initialize an integer (
linesCount
) to keep count of the resultant lines after justification. - Call
justifyText
passing the phrases, their count, the maxWidth, and the address oflinesCount
. - Process the returned array of justified text lines.
Memory Management
- Memory management is carefully handled using
malloc
andrealloc
functions to allocate and adjust memory as needed. - It is important to ensure that allocated memory is appropriately freed after use to avoid memory leaks.
The code effectively demonstrates managing string operations, memory allocation, and algorithmic logic to solve a complex real-world problem such as text justification in programming.
var justifyText = function (wordsArray, maxWidth) {
let result = [];
let index = 0;
while (index < wordsArray.length) {
let lineWords = fetchWords(index, wordsArray, maxWidth);
index += lineWords.length;
result.push(formatLine(lineWords, index, wordsArray, maxWidth));
}
return result;
function fetchWords(startIndex, words, lineMaxWidth) {
let line = [];
let lineLength = 0;
while (startIndex < words.length && lineLength + words[startIndex].length <= lineMaxWidth) {
line.push(words[startIndex]);
lineLength += words[startIndex].length + 1; // add space
startIndex++;
}
return line;
}
function formatLine(line, idx, wordList, maxWidth) {
let totalLength = -1;
line.forEach(word => {
totalLength += word.length + 1; // add spaces
});
let additionalSpaces = maxWidth - totalLength;
if (line.length === 1 || idx === wordList.length) {
return line.join(" ") + " ".repeat(additionalSpaces);
}
let wordsInLine = line.length - 1;
let evenSpace = Math.floor(additionalSpaces / wordsInLine);
let additionalSpaceCount = additionalSpaces % wordsInLine;
for (let j = 0; j < additionalSpaceCount; j++) {
line[j] += " ";
}
for (let j = 0; j < wordsInLine; j++) {
line[j] += " ".repeat(evenSpace);
}
return line.join(" ");
}
};
In the given JavaScript solution, you are presented with a function justifyText
designed to justify text within a given width constraint. The function achieves the task of text justification by ensuring each line of text fits exactly within the specified maxWidth
, aligning spaces appropriately between words.
At the start,
justifyText
initializes an empty arrayresult
to collect the formatted lines and uses an index to track the current position in the inputwordsArray
.The main process loops over
wordsArray
to collect and justify lines until all words are processed:- It calls
fetchWords
to gather as many words as fit intomaxWidth
without breaking any word. Space between words is accounted for in the measure of line width. - It then updates the index by adding the number of words collected for the current line.
formatLine
is invoked with the words to arrange them into a justified format where necessary spaces are added. The line is then added to theresult
array.
- It calls
The helper function
fetchWords
:- Starts with an empty array for the line and a counter for the line length.
- It iterates through words starting from a given index, adding words to the line if they fit when including a space for separation.
- Returns the line once the
maxWidth
is met or exceeds.
The
formatLine
function:- First calculates the total length of all words plus the spaces in the line.
- Computes the number of additional spaces needed to make the line length equal to
maxWidth
. - If the line is the last one or contains just one word, the spaces are added at the end of the line.
- For other lines, it calculates even distribution of spaces and allocates any extra spaces to the early words in the line.
- Each word (except the last one) is then extended by adding calculated spaces, thus justifying the text.
The function finally returns the
result
array containing all justified lines.
This function is efficient for creating a text block aligned both left and right by ensuring optimal distribution of spaces, conforming to the constraints of text justification. Remember, the solution works directly for arrays of words and specified maximum widths without requiring pre-formatting or additional handling of string inputs.
class Solution:
def justifyText(self, words: List[str], maxWidth: int) -> List[str]:
def gather_words(index):
line_content = []
current_width = 0
while index < len(words) and current_width + len(words[index]) <= maxWidth:
line_content.append(words[index])
current_width += len(words[index]) + 1
index += 1
return line_content
def format_line(line_words, index):
current_line_length = -1
for word in line_words:
current_line_length += len(word) + 1
available_space = maxWidth - current_line_length
if len(line_words) == 1 or index == len(words):
return " ".join(line_words) + " " * available_space
num_of_spaces = len(line_words) - 1
even_space = available_space // num_of_spaces
extra_space_count = available_space % num_of_spaces
for j in range(extra_space_count):
line_words[j] += " "
for j in range(num_of_spaces):
line_words[j] += " " * even_space
return " ".join(line_words)
justified_text = []
current_index = 0
while current_index < len(words):
words_in_line = gather_words(current_index)
current_index += len(words_in_line)
justified_text.append(format_line(words_in_line, current_index))
return justified_text
This Python3 solution involves creating a class Solution
with a method justifyText
that formats a list of words into a justified text layout. The function takes a list of words and a maxWidth
, and orchestrates each line's width to match maxWidth
precisely.
Key Functions:
gather_words(index)
: Collects words that can fit withinmaxWidth
starting from a given index. It handles accumulation of words and ensures that they do not exceed the providedmaxWidth
.format_line(line_words, index)
: Assembles a single line from the given words. It adjusts spaces between words to distribute them evenly across the line. If a single word exists in the line or it's the last line, spaces are added only at the end. For other cases, spaces are divided evenly with additional spaces added to earlier words if they do not divide cleanly.
Process Flow:
- Initialize an empty list
justified_text
to hold each formatted line. - Use a while loop to iterate over words using an index
current_index
initially set to zero. - Inside the loop, retrieve words fitting in a single line using
gather_words
. - Adjust format and spacing of these words using
format_line
. - Append the formatted line to
justified_text
and increment the index by the number of words just processed. - Return the
justified_text
containing all formatted lines.
Output:
The output of the method is a list of strings, where each string represents a line of text justified to match the specified maxWidth
. This allows for presentation of text in a visually cohesive manner, maintaining a clean, aligned look on both the left and right margins across multiple lines of text.
No comments yet.