Text Justification- DSA Solution

Spread the love
A close-up shot of a person coding on a laptop, focusing on the hands and screen.

Format the text so that each line has precisely W characters, left and right justified, given an array of strings arr[] and a width W. Put as many words as you can in each line, try to divide the additional spaces evenly, and if they don’t divide evenly, put more spaces to the left. There should be no extra spaces between words in the final line, which should be left-justified.

For instance:

Input: arr[] = {“GfG”, “is”, “the”, “best”, “computer”, “science”, “portal”, “for”, “geeks.”}, W = 16 
Output: 
{
“GfG  is the best”,
“computer science”,
“portal       for”,
“geeks.          “
}
Arr[] = {"GfG", "is", "the", "best", "computer", "science", "portal", "for", "geeks."}, W = 16 is the input. 
The output is: "GfG is the best," "computer science," "portal for," and "geeks."
Justification: The text must be justified so that each line has precisely 16 characters and is evenly justified. 


The first line's total character count is 12 (including spaces: 12 + 3 = 15) = "GfG" (3) + "is" (2) + "the" (3) + "best" (4). We add one more space between "GfG" and "is" because we need one more space and all additional spaces should be added from left to right.
The second line's total character count is 15 (including spaces: 15 + 1 = 16) = "computer" (8) + "science" (7). There's no need for additional spaces.
The third line has a total of nine characters (including spaces): "portal" (6) + "for" (3) = 10. We shall insert the six extra spaces between "portal" and "for" since we need to add them.
The fourth line's total character count is "geeks." (6) = 6. We will add the final ten spaces at the end because the final line needs to be left justified.
W = 11; input: arr[] = {"The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog."} 
The output is: { "The quick," "brown fox," "jumps over," "the lazy," and "dog."
Output: 
{
“The   quick”,
“brown   fox”,
“jumps  over”,
 “the    lazy”,
“dog.       “
}

Table of Contents

Method:

The initial step is to decide which words, with one space between each pair of words, can be put into each line. Each line must be justified after the words have been chosen.

The total length of the words that are contained, separated by one space, must be less than or equal to W in order for a line to be justified. Additionally, if the current line is the text’s last line, we must add spaces to it so that its width equals W. If not, count the number of spaces required to make each line W long and evenly distribute them if the current line is not the last line.

C++

// C++ program to Justify the given Text
// according to the given width of each line

#include <bits/stdc++.h>
using namespace std;

// function to count the characters from arr[start] to
// arr[end], both start and end inclusive
int countCharacters(int start, int end, vector<string> &arr) {
    int cnt = 0;
    for (int i = start; i <= end; i++)
        cnt += arr[i].length();
    return cnt;
}

// function to fill the end of str with spaces
string padLine(string &str, int W) {
    int len = str.length();
    for (int i = 0; i < W - len; i++)
        str.push_back(' ');
    return str;
}

// function to form a line using all the words in range [start, end]
// and justify the line to push it to the result
string justify(int start, int end, vector<string> &arr, int W) {
    string justifiedLine = "";
    int wordsCnt = end - start + 1;

    // If the line has only 1 word, then pad it with spaces and return it
    if (wordsCnt == 1)
        return padLine(arr[start], W);

    int spaceCnt = W - countCharacters(start, end, arr);
    string space = " ";
    int extraSpaces = 0;

    // If the line is not the last line, then all words should have
    // even spaces between them and all the extra spaces will be
    // added to the spaces to the left
    if (end != arr.size() - 1) {
        space = string(spaceCnt / (wordsCnt - 1), ' ');
        extraSpaces = spaceCnt % (wordsCnt - 1);
    }

    for (int i = start; i <= end; i++) {

        // Add the word to the justified line
        justifiedLine.append(arr[i]);

        // add spaces to the justified line
        justifiedLine.append(space);

        // If there are extra spaces, add it to the spaces to the left
        if (extraSpaces > 0) {
            justifiedLine.push_back(' ');
            extraSpaces -= 1;
        }
    }

    // Remove extra spaces from the right end
    while (justifiedLine.length() > W)
        justifiedLine.pop_back();

    // Pad line with spaces if we have empty space to the right
    return padLine(justifiedLine, W);
}

// function to get the end index such that all the words from
// start to end can be placed in one line
int getEnd(int start, vector<string> &arr, int W) {
    int end = start + 1;
    int len = arr[start].length();
    while (end < arr.size() && (len + 1 + arr[end].length()) <= W) {
        len += arr[end].length() + 1;
        end += 1;
    }
    return end - 1;
}

// function to combine the words to each line and justify the line
// from both sides
vector<string> justifyText(vector<string> &arr, int W) {
    int start = 0, end;
    vector<string> justifiedText;

    while (start < arr.size()) {

        // find the ending index such that words in the range 
        // [start, end] can be put in a single line
        end = getEnd(start, arr, W);

        // Form a line using words from start to end and justify it
        string justifiedLine = justify(start, end, arr, W);

        // Push the justified line to the result
        justifiedText.push_back(justifiedLine);
        start = end + 1;
    }
    return justifiedText;
}

int main() {
    vector<string> arr = {"Sky", "is", "the", "best", "computer", 
                          "science", "portal", "for", "Webz."} ;
    int W = 16;

    vector<string> ans = justifyText(arr, W);
    for (int i = 0; i < ans.size(); i++)
        cout << ans[i] << "\n";
    return 0;
}

Java

// Java program to justify the given text
// according to the given width of each line

import java.util.ArrayList;
import java.util.List;
public class GFG {

    // function to count the characters from arr[start] to
    // arr[end], both start and end inclusive
    static int countCharacters(int start, int end, 
                                          List<String> arr) {
        int cnt = 0;
        for (int i = start; i <= end; i++)
            cnt += arr.get(i).length();
        return cnt;
    }

    // function to fill the end of str with spaces
    static String padLine(String str, int W) {
        int len = str.length();
        StringBuilder sb = new StringBuilder(str);
        for (int i = 0; i < W - len; i++)
            sb.append(' ');
        return sb.toString();
    }

    // function to form a line using all the words in range [start, end]
    // and justify the line to push it to the result
    static String justify(int start, int end, List<String> arr, int W) {
        StringBuilder justifiedLine = new StringBuilder();
        int wordsCnt = end - start + 1;

        // If the line has only 1 word, then pad it with spaces and return it
        if (wordsCnt == 1)
            return padLine(arr.get(start), W);

        int spaceCnt = W - countCharacters(start, end, arr);
        String space = " ";
        int extraSpaces = 0;

        // If this line is not the last line, then all the words should
        // have even spaces between them and all the extra spaces will
        // be added to the spaces to the left
        if (end != arr.size() - 1) {
            space = new String(new char[spaceCnt / (wordsCnt - 1)])
                                                      .replace('\0', ' ');
            extraSpaces = spaceCnt % (wordsCnt - 1);
        }

        for (int i = start; i <= end; i++) {

            // Add the word to the justified line
            justifiedLine.append(arr.get(i));

            // add spaces to the justified line
            justifiedLine.append(space);

            // If there are extra spaces, add it to the spaces to the left
            if (extraSpaces > 0) {
                justifiedLine.append(' ');
                extraSpaces -= 1;
            }
        }

        // Remove extra spaces from the right end
        while (justifiedLine.length() > W)
            justifiedLine.setLength(justifiedLine.length() - 1);

        // Pad line with spaces if we have empty space to the right
        return padLine(justifiedLine.toString(), W);
    }

    // function to get the end index such that all the words from
    // start to end can be placed in one line
    static int getEnd(int start, List<String> arr, int W) {
        int end = start + 1;
        int len = arr.get(start).length();
        while (end < arr.size() && (len + 1 + arr.get(end).length()) <= W) {
            len += arr.get(end).length() + 1;
            end += 1;
        }
        return end - 1;
    }

    // function to combine the words to each line and justify the line
    // from both sides
    static List<String> justifyText(List<String> arr, int W) {
        int start = 0;
        List<String> justifiedText = new ArrayList<>();

        while (start < arr.size()) {

            // find the ending index such that words in the range
            // [start, end] can be put in a single line
            int end = getEnd(start, arr, W);

            // Form a line using words from start to end and justify it
            String justifiedLine = justify(start, end, arr, W);

            // Push the justified line to the result
            justifiedText.add(justifiedLine);
            start = end + 1;
        }
        return justifiedText;
    }

    public static void main(String[] args) {
        List<String> arr = List.of("Sky", "is", "the", "best", 
                "computer", "science", "portal", "for", "Webz." );
        int W = 16;

        List<String> ans = justifyText(arr, W);
        for (String line : ans)
            System.out.println(line);
    }
}

Python

# Python program to justify the given text
# according to the given width of each line

# function to count the characters from arr[start] to
# arr[end], both start and end inclusive
def count_characters(start, end, arr):
    cnt = 0
    for i in range(start, end + 1):
        cnt += len(arr[i])
    return cnt


# function to fill the end of str with spaces
def pad_line(s, W):
    len_s = len(s)
    return s + ' ' * (W - len_s)


# function to form a line using all the words in range [start, end]
# and justify the line to push it to the result
def justify(start, end, arr, W):
    justified_line = ""
    words_cnt = end - start + 1

    # If the line has only 1 word, then pad it with spaces and return it
    if words_cnt == 1:
        return pad_line(arr[start], W)

    space_cnt = W - count_characters(start, end, arr)
    space = " "
    extra_spaces = 0

    # If the line is not the last line, then all words should have
    # even spaces between them and all the extra spaces will be
    # added to the spaces to the left
    if end != len(arr) - 1:
        space = ' ' * (space_cnt // (words_cnt - 1))
        extra_spaces = space_cnt % (words_cnt - 1)

    for i in range(start, end + 1):
      
        # Add the word to the justified line
        justified_line += arr[i]

        # add spaces to the justified line
        if i < end:
            justified_line += space

            # If there are extra spaces, add it to the spaces to the left
            if extra_spaces > 0:
                justified_line += ' '
                extra_spaces -= 1

    # Remove extra spaces from the right end
    justified_line = justified_line[:W]

    # Pad line with spaces if we have empty space to the right
    return pad_line(justified_line, W)


# function to get the end index such that all the words from
# start to end can be placed in one line
def get_end(start, arr, W):
    end = start + 1
    len_words = len(arr[start])
    while end < len(arr) and (len_words + 1 + len(arr[end])) <= W:
        len_words += len(arr[end]) + 1
        end += 1
    return end - 1

# function to combine the words to each line and justify the line
# from both sides
def justify_text(arr, W):
    start = 0
    justified_text = []

    while start < len(arr):
        # find the ending index such that words in the range
        # [start, end] can be put in a single line
        end = get_end(start, arr, W)

        # Form a line using words from start to end and justify it
        justified_line = justify(start, end, arr, W)

        # Push the justified line to the result
        justified_text.append(justified_line)
        start = end + 1

    return justified_text

if __name__ == "__main__":
    arr = ["Sky", "is", "the", "best", "computer",
               "science", "portal", "for", "Webz."]
    W = 16

    ans = justify_text(arr, W)
    for line in ans:
        print(line)

C#

// C# program to Justify the given Text
// according to the given width of each line

using System;
using System.Collections.Generic;
using System.Text;

class GfG {
  
    // Function to count the characters from arr[start] to
    // arr[end], both start and end inclusive
    static int CountCharacters(int start, int end, string[] arr) {
        int cnt = 0;
        for (int i = start; i <= end; i++)
            cnt += arr[i].Length;
        return cnt;
    }

    // Function to fill the end of str with spaces
    static string PadLine(string str, int W) {
        int len = str.Length;
        StringBuilder sb = new StringBuilder(str);
        for (int i = 0; i < W - len; i++) {
            sb.Append(' ');
        }
        return sb.ToString();
    }

    // Function to form a line using all the words in range [start, end]
    // and justify the line to push it to the result
    static string Justify(int start, int end, string[] arr, int W) {
        StringBuilder justifiedLine = new StringBuilder();
        int wordsCnt = end - start + 1;

        // If the line has only 1 word, then pad it with spaces and return it
        if (wordsCnt == 1)
            return PadLine(arr[start], W);

        int spaceCnt = W - CountCharacters(start, end, arr);
        string space = " ";
        int extraSpaces = 0;

        // If the line is not the last line, then all words should have
        // even spaces between them and all the extra spaces will be
        // added to the spaces to the left
        if (end != arr.Length - 1) {
            space = new string(' ', spaceCnt / (wordsCnt - 1));
            extraSpaces = spaceCnt % (wordsCnt - 1);
        }

        for (int i = start; i <= end; i++) {
          
            // Add the word to the justified line
            justifiedLine.Append(arr[i]);

            // Add spaces to the justified line
            if (i < end) {
                justifiedLine.Append(space);

                // If there are extra spaces, add it to the spaces to the left
                if (extraSpaces > 0) {
                    justifiedLine.Append(' ');
                    extraSpaces -= 1;
                }
            }
        }

        // Remove extra spaces from the right end
        if (justifiedLine.Length > W)
            justifiedLine.Length = W;

        // Pad line with spaces if we have empty space to the right
        return PadLine(justifiedLine.ToString(), W);
    }

    // Function to get the end index such that all the words from
    // start to end can be placed in one line
    static int GetEnd(int start, string[] arr, int W) {
        int end = start + 1;
        int len = arr[start].Length;
        while (end < arr.Length && (len + 1 + arr[end].Length) <= W) {
            len += arr[end].Length + 1;
            end += 1;
        }
        return end - 1;
    }

    // Function to combine the words to each line and justify the line
    // from both sides
    static List<string> JustifyText(string[] arr, int W) {
        int start = 0;
        List<string> justifiedText = new List<string>();

        while (start < arr.Length) {
          
            // Find the ending index such that words in the range
            // [start, end] can be put in a single line
            int end = GetEnd(start, arr, W);

            // Form a line using words from start to end and justify it
            string justifiedLine = Justify(start, end, arr, W);

            // Push the justified line to the result
            justifiedText.Add(justifiedLine);
            start = end + 1;
        }

        return justifiedText;
    }

    static void Main() {
        string[] arr = {
          "Sky", "is", "the", "best", "computer", 
          "science", "portal", "for", "Webz."
        }; 
        int W = 16;

        List<string> ans = JustifyText(arr, W);
        foreach (string line in ans) {
            Console.WriteLine(line);
        }
    }
}

JS

// JavaScript program to Justify the given Text
// according to the given width of each line

// Function to count the characters from arr[start] to
// arr[end], both start and end inclusive
function countCharacters(start, end, arr) {
    let cnt = 0;
    for (let i = start; i <= end; i++) {
        cnt += arr[i].length;
    }
    return cnt;
}

// Function to fill the end of str with spaces
function padLine(str, W) {
    let len = str.length;
    while (len < W) {
        str += ' ';
        len++;
    }
    return str;
}

// Function to form a line using all the words in range [start, end]
// and justify the line to push it to the result
function justify(start, end, arr, W) {
    let justifiedLine = "";
    let wordsCnt = end - start + 1;

    // If the line has only 1 word, then pad it with spaces and return it
    if (wordsCnt === 1) {
        return padLine(arr[start], W);
    }

    let spaceCnt = W - countCharacters(start, end, arr);
    let space = " ";
    let extraSpaces = 0;

    // If the line is not the last line, then all words should have
    // even spaces between them and all the extra spaces will be
    // added to the spaces to the left
    if (end !== arr.length - 1) {
        space = " ".repeat(spaceCnt / (wordsCnt - 1));
        extraSpaces = spaceCnt % (wordsCnt - 1);
    }

    for (let i = start; i <= end; i++) {
        // Add the word to the justified line
        justifiedLine += arr[i];

        // Add spaces to the justified line
        if (i < end) { // Avoid adding space after the last word
            justifiedLine += space;

            // If there are extra spaces, add them to the spaces to the left
            if (extraSpaces > 0) {
                justifiedLine += ' ';
                extraSpaces--;
            }
        }
    }

    // Remove extra spaces from the right end
    if (justifiedLine.length > W) {
        justifiedLine = justifiedLine.slice(0, W);
    }

    // Pad line with spaces if we have empty space to the right
    return padLine(justifiedLine, W);
}

// Function to get the end index such that all the words from
// start to end can be placed in one line
function getEnd(start, arr, W) {
    let end = start + 1;
    let len = arr[start].length;
    while (end < arr.length && (len + 1 + arr[end].length) <= W) {
        len += arr[end].length + 1;
        end++;
    }
    return end - 1;
}

// Function to combine the words to each line and justify the line
// from both sides
function justifyText(arr, W) {
    let start = 0;
    let justifiedText = [];

    while (start < arr.length) {
        // Find the ending index such that words in the range
        // [start, end] can be put in a single line
        let end = getEnd(start, arr, W);

        // Form a line using words from start to end and justify it
        let justifiedLine = justify(start, end, arr, W);

        // Push the justified line to the result
        justifiedText.push(justifiedLine);
        start = end + 1;
    }

    return justifiedText;
}

let arr = ["Sky", "is", "the", "best", "computer",
            "science", "portal", "for", "webz."];
let W = 16;

let ans = justifyText(arr, W);
for (let line of ans) {
    console.log(line);
}

Output

Sky  is the best
computer science
portal for
Webz.

Time Complexity: O(N), where is the sum of length of all words.
Auxiliary Space: O(W), where is the max width of a line.