# Text Justification
**Difficulty:** HARD
[External](https://leetcode.com/problems/text-justification)
Canonical: https://scaleengineer.com/dsa/problems/text-justification
**Data structures:** Array, String
**Companies:** [Adobe](https://scaleengineer.com/companies/adobe), [Airbnb](https://scaleengineer.com/companies/airbnb), [Amazon](https://scaleengineer.com/companies/amazon), [Apple](https://scaleengineer.com/companies/apple), [Atlassian](https://scaleengineer.com/companies/atlassian), [Bloomberg](https://scaleengineer.com/companies/bloomberg), [Google](https://scaleengineer.com/companies/google), [Karat](https://scaleengineer.com/companies/karat), [LinkedIn](https://scaleengineer.com/companies/linkedin), [Meta](https://scaleengineer.com/companies/meta), [Microsoft](https://scaleengineer.com/companies/microsoft), [Oracle](https://scaleengineer.com/companies/oracle), [PayPal](https://scaleengineer.com/companies/paypal), [Roblox](https://scaleengineer.com/companies/roblox), [TikTok](https://scaleengineer.com/companies/tiktok), [Uber](https://scaleengineer.com/companies/uber), [Visa](https://scaleengineer.com/companies/visa), [Zoho](https://scaleengineer.com/companies/zoho), [Capital One](https://scaleengineer.com/companies/capital-one), [Netflix](https://scaleengineer.com/companies/netflix), [Databricks](https://scaleengineer.com/companies/databricks), [Twilio](https://scaleengineer.com/companies/twilio), [MongoDB](https://scaleengineer.com/companies/mongodb), [Samsara](https://scaleengineer.com/companies/samsara), [Coursera](https://scaleengineer.com/companies/coursera), [SIG](https://scaleengineer.com/companies/sig), [Moveworks](https://scaleengineer.com/companies/moveworks), [Coinbase](https://scaleengineer.com/companies/coinbase), [DevRev](https://scaleengineer.com/companies/devrev), [Notion](https://scaleengineer.com/companies/notion), [Robinhood](https://scaleengineer.com/companies/robinhood), [Sentry](https://scaleengineer.com/companies/sentry), [WeRide](https://scaleengineer.com/companies/weride)
---
## Problem
Given an array of strings `words` and a width `maxWidth`, format the text such that each line has exactly `maxWidth` characters and is fully (left and right) justified.

You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces `' '` when necessary so that each line has exactly `maxWidth` characters.

Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line does not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right.

For the last line of text, it should be left-justified, and no extra space is inserted between words.

**Note:**

* A word is defined as a character sequence consisting of non-space characters only.
* Each word's length is guaranteed to be greater than `0` and not exceed `maxWidth`.
* The input array `words` contains at least one word.

**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`

# Approaches
## Greedy Line-by-Line Simulation
This problem requires a greedy approach to pack words into lines and then format them. The most direct way to solve this is to simulate the process line by line. For each line, we first determine the maximum number of words that can fit, and then we apply the specific justification rules based on whether it's a regular line, the last line, or a line with a single word.
**Time:** O(N * maxWidth) · **Space:** O(N * maxWidth)
**Pros:** It's a direct simulation of the process described in the problem.; It's efficient and passes within the given constraints.; The greedy choice at each step (packing as many words as possible) is exactly what the problem requires.
**Cons:** The logic can be tricky to implement correctly, with several edge cases to handle (last line, single-word line, space distribution).; The code can become lengthy due to the different formatting rules.
### Explanation
The algorithm iterates through the words, processing one line at a time. In each step, it identifies a contiguous block of words that can fit onto a single line. Once these words are identified, it calculates the necessary spacing and constructs the formatted line string. This process is repeated until all words have been placed.

*   **Algorithm Steps:**

    1.  Initialize an empty list `result` to store the formatted lines and an index `i = 0` to track the current word.
    2.  Loop while `i` is less than the total number of words:
        a.  **Determine words for the current line:** Greedily find the range of words `[i, j-1]` that can fit on the current line. Start with `words[i]` and keep adding subsequent words as long as the total length (including at least one space between words) does not exceed `maxWidth`.
        b.  **Format the identified line:**
            *   **Left Justification:** If the line is the last line of text (`j` reaches the end of the words array) or contains only a single word, it should be left-justified. This means words are separated by a single space, and the rest of the line is padded with spaces on the right.
            *   **Full Justification:** For all other lines, the spaces need to be distributed as evenly as possible between the words. Calculate the base number of spaces for each gap and distribute any extra spaces one by one to the gaps on the left.
        c.  Add the newly formatted line to the `result` list.
        d.  Update the index `i` to `j` to start processing the next line.
    3.  Return the `result` list.

*   **Code Snippet:**

    ```java
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<String> fullJustify(String[] words, int maxWidth) {
        List<String> result = new ArrayList<>();
        int i = 0;
        int n = words.length;

        while (i < n) {
            // 1. Determine words for the current line
            int j = i + 1;
            int lineLength = words[i].length();
            while (j < n && (lineLength + words[j].length() + (j - i)) <= maxWidth) {
                lineLength += words[j].length();
                j++;
            }

            int numWords = j - i;
            int totalSpaces = maxWidth - lineLength;

            StringBuilder line = new StringBuilder();

            // 2. Format the line
            // Case 1: Last line or single-word line (Left Justification)
            if (numWords == 1 || j == n) {
                line.append(words[i]);
                for (int k = i + 1; k < j; k++) {
                    line.append(" ").append(words[k]);
                }
                int remainingSpaces = maxWidth - line.length();
                appendSpaces(line, remainingSpaces);
            } else {
            // Case 2: Regular line (Full Justification)
                int numGaps = numWords - 1;
                int spacesPerGap = totalSpaces / numGaps;
                int extraSpaces = totalSpaces % numGaps;

                line.append(words[i]);
                for (int k = i + 1; k < j; k++) {
                    int spacesToApply = spacesPerGap + (extraSpaces > 0 ? 1 : 0);
                    appendSpaces(line, spacesToApply);
                    line.append(words[k]);
                    extraSpaces--;
                }
            }
            
            result.add(line.toString());
            i = j; // Move to the start of the next line
        }

        return result;
    }

    private void appendSpaces(StringBuilder sb, int count) {
        for (int k = 0; k < count; k++) {
            sb.append(" ");
        }
    }
}
    ```
### Algorithm
*   Initialize an empty list `result` to store the formatted lines and an index `i = 0` to track the current word.
*   Loop while `i` is less than the total number of words:
    *   **Determine words for the current line:** Greedily find the range of words `[i, j-1]` that can fit on the current line. Start with `words[i]` and keep adding subsequent words as long as the total length (including at least one space between words) does not exceed `maxWidth`.
    *   **Format the identified line:**
        *   **Left Justification:** If the line is the last line of text (`j` reaches the end of the words array) or contains only a single word, it should be left-justified. This means words are separated by a single space, and the rest of the line is padded with spaces on the right.
        *   **Full Justification:** For all other lines, the spaces need to be distributed as evenly as possible between the words. Calculate the base number of spaces for each gap and distribute any extra spaces one by one to the gaps on the left.
    *   Add the newly formatted line to the `result` list.
    *   Update the index `i` to `j` to start processing the next line.
*   Return the `result` list.

# Solutions
### CSharp

```csharp
public class Solution {
    public IList < string > FullJustify(string[] words, int maxWidth) {
        var ans = new List < string > ();
        for (int i = 0, n = words.Length; i < n;) {
            var t = new List < string > ();
            t.Add(words[i]);
            int cnt = words[i].Length;
            ++i;
            while (i < n && cnt + 1 + words[i].Length <= maxWidth) {
                t.Add(words[i]);
                cnt += 1 + words[i].Length;
                ++i;
            }
            if (i == n || t.Count == 1) {
                string left = string.Join(" ", t);
                string right = new string(' ', maxWidth - left.Length);
                ans.Add(left + right);
                continue;
            }
            int spaceWidth = maxWidth - (cnt - t.Count + 1);
            int w = spaceWidth / (t.Count - 1);
            int m = spaceWidth % (t.Count - 1);
            var row = new StringBuilder();
            for (int j = 0; j < t.Count - 1; ++j) {
                row.Append(t[j]);
                row.Append(new string(' ', w + (j < m ? 1 : 0)));
            }
            row.Append(t[t.Count - 1]);
            ans.Add(row.ToString());
        }
        return ans;
    }
}
```

### Java

```java
class Solution {
public
  List<String> fullJustify(String[] words, int maxWidth) {
    List<String> ans = new ArrayList<>();
    for (int i = 0, n = words.length; i < n;) {
      List<String> t = new ArrayList<>();
      t.add(words[i]);
      int cnt = words[i].length();
      ++i;
      while (i < n && cnt + 1 + words[i].length() <= maxWidth) {
        cnt += 1 + words[i].length();
        t.add(words[i++]);
      }
      if (i == n || t.size() == 1) {
        String left = String.join(" ", t);
        String right = " ".repeat(maxWidth - left.length());
        ans.add(left + right);
        continue;
      }
      int spaceWidth = maxWidth - (cnt - t.size() + 1);
      int w = spaceWidth / (t.size() - 1);
      int m = spaceWidth % (t.size() - 1);
      StringBuilder row = new StringBuilder();
      for (int j = 0; j < t.size() - 1; ++j) {
        row.append(t.get(j));
        row.append(" ".repeat(w + (j < m ? 1 : 0)));
      }
      row.append(t.get(t.size() - 1));
      ans.add(row.toString());
    }
    return ans;
  }
}

```

### CPP

```cpp
class Solution {
public:
  vector<string> fullJustify(vector<string> &words, int maxWidth) {
    vector<string> ans;
    for (int i = 0, n = words.size(); i < n;) {
      vector<string> t = {words[i]};
      int cnt = words[i].size();
      ++i;
      while (i < n && cnt + 1 + words[i].size() <= maxWidth) {
        cnt += 1 + words[i].size();
        t.emplace_back(words[i++]);
      }
      if (i == n || t.size() == 1) {
        string left = t[0];
        for (int j = 1; j < t.size(); ++j) {
          left += " " + t[j];
        }
        string right = string(maxWidth - left.size(), ' ');
        ans.emplace_back(left + right);
        continue;
      }
      int spaceWidth = maxWidth - (cnt - t.size() + 1);
      int w = spaceWidth / (t.size() - 1);
      int m = spaceWidth % (t.size() - 1);
      string row;
      for (int j = 0; j < t.size() - 1; ++j) {
        row += t[j] + string(w + (j < m ? 1 : 0), ' ');
      }
      row += t.back();
      ans.emplace_back(row);
    }
    return ans;
  }
};

```

### Python

```python
''' Explanation: - The function `fullJustify` takes a list of words and a maximum width `maxWidth` as inputs. - It iterates over each word, deciding whether to add the current word to the current line (`cur`) or to start a new line. - If adding the current word to the line would exceed `maxWidth`, it justifies the current line by adding extra spaces between words as needed, then starts a new line. - Once all words are processed, it left-justifies the last line by joining the remaining words in `cur` with a single space and then using `.ljust(maxWidth)` to ensure the line is of maximum width. - The result is a list of strings, where each string represents a justified line of text. This solution carefully handles edge cases, such as when there's only one word in the line (avoiding division by zero) and ensuring the last line is left-justified instead of fully justified. ''' ''' >>> cur = [1] >>> len(cur) - 1 0 >>> ( len(cur) - 1 or 1 ) 1 ''' ''' # api: string.ljust(width[, fillchar]) >>> "Hello".ljust(10) 'Hello ' >>> "Hello".ljust(10, '-') 'Hello-----' >>> "Hello".ljust(2) 'Hello' >>> "Hello World".ljust(20) 'Hello World ' >>> "Hello World".ljust(20, '-') 'Hello World---------' # api: string.rjust(width[, fillchar]) >>> "Hello".rjust(10) ' Hello' >>> "Hello".rjust(10, '-') '-----Hello' >>> "Hello".rjust(2) 'Hello' >>> "Hello World".rjust(20) ' Hello World' >>> "Hello World".rjust(20, '-') '---------Hello World' ''' class Solution : def fullJustify ( self , words : List [ str ], maxWidth : int ) -> List [ str ]: res , cur , num_of_letters = [], [], 0 for w in words : # len(cur) is the space count, from last round, right one less after adding 'w' if num_of_letters + len ( cur ) + len ( w ) > maxWidth : for i in range ( maxWidth - num_of_letters ): # The "or 1" part is for dealing with the edge case 'len(cur) == 1' cur [ i % ( len ( cur ) - 1 or 1 )] += ' ' res . append ( '' . join ( cur )) cur , num_of_letters = [], 0 cur += [ w ] num_of_letters += len ( w ) return res + [ ' ' . join ( cur ). ljust ( maxWidth )] ############ ''' >>> 17 % 3 2 >>> divmod(17,3) (5, 2) ''' ''' >>> ['This', 'is', 'an'] ['This', 'is', 'an'] >>> t = ['This', 'is', 'an'] >>> spaces = [' ', ' '] # note: here j starts from 0 >>> for j, word in enumerate(t[1:]): ... print(j) ... print(word) ... 0 is 1 an ''' class Solution : def fullJustify ( self , words : List [ str ], maxWidth : int ) -> List [ str ]: def partition ( n , cnt ): base , mod = divmod ( n , cnt ) res = [ f " { ' ' * ( base + ( i < mod )) } " for i in range ( cnt )] return res ans = [] i , n = 0 , len ( words ) while i < n : # one line per iteration t = [ words [ i ]] cnt = len ( words [ i ]) i += 1 # move to next word while i < n and cnt + 1 + len ( words [ i ]) <= maxWidth : # greedy search for one line cnt += 1 + len ( words [ i ]) t . append ( words [ i ]) i += 1 if i == n or len ( t ) == 1 : # this is the last line or only one (super-long) word in a line left = ' ' . join ( t ) right = ' ' * ( maxWidth - len ( left )) ans . append ( f " { left }{ right } " ) continue # so i != n , so only one word, no need to add spaces. # e.g. a word with the same length as maxWidth, # or, e.g. the 2nd word is super long and cannot fit in current line # if i==n, then will not enter next while words_width = cnt - ( len ( t ) - 1 ) # pure words total length, with no spaces # spaces in-between these words in t[]: len(t) - 1 space_width = maxWidth - words_width spaces = partition ( space_width , len ( t ) - 1 ) sb = [ t [ 0 ]] # 1st word of this line, no space before it for j , word in enumerate ( t [ 1 :]): # last word has no following space sb . append ( spaces [ j ]) # j starts from 0 sb . append ( word ) ans . append ( '' . join ( sb )) return ans ########### class Solution : def fullJustify ( self , words : List [ str ], maxWidth : int ) -> List [ str ]: left = 0 result = [] while left < len ( words ): right = self . findRight ( left , words , maxWidth ) result . append ( self . justify ( left , right , words , maxWidth )) left = right + 1 return result def findRight ( self , left , words , maxWidth ): right = left word_length = len ( words [ right ]) while ( right + 1 ) < len ( words ) and ( word_length + 1 + len ( words [ right + 1 ])) <= maxWidth : right += 1 word_length += 1 + len ( words [ right ]) return right def justify ( self , left , right , words , maxWidth ): if right - left == 0 : return self . padResult ( words [ left ], maxWidth ) is_last_line = ( right == len ( words ) - 1 ) num_spaces = right - left total_space = maxWidth - self . wordsLength ( left , right , words ) space = " " if is_last_line else " " * ( total_space // num_spaces ) remainder = 0 if is_last_line else total_space % num_spaces result = "" for i in range ( left , right ): result += words [ i ] result += space if remainder > 0 : result += " " remainder -= 1 result += words [ right ] result += " " * ( maxWidth - len ( result )) return result def wordsLength ( self , left , right , words ): words_length = 0 for i in range ( left , right + 1 ): words_length += len ( words [ i ]) return words_length def padResult ( self , result , maxWidth ): return result + " " * ( maxWidth - len ( result ))
```
