package com.freetymekiyan.algorithms.level.hard;
import org.junit.Test;
import java.util.LinkedList;
import java.util.List;
/**
* Given an array of words and a length L, format the text such that each line has exactly L characters and is fully
* (left and right) justified.
* <p>
* 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 L characters.
* <p>
* Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not
* divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right.
* <p>
* For the last line of text, it should be left justified and no extra space is inserted between words.
* <p>
* For example,
* words: ["This", "is", "an", "example", "of", "text", "justification."]
* L: 16.
* <p>
* Return the formatted lines as:
* [
* "This is an",
* "example of text",
* "justification. "
* ]
* Note: Each word is guaranteed not to exceed L in length.
* <p>
* Corner Cases:
* A line other than the last line might contain only one word. What should you do in this case?
* In this case, that line should be left-justified.
* <p>
* Company Tags: LinkedIn, Airbnb, Facebook
* Tags: String
*/
public class TextJustification {
/**
* String.
* First figure out how many words to fit current line.
* | Init a length as -1, to exclude the last space.
* | Start from i, add word length + 1 to length as long as w is still within array and length within maxWidth.
* Then append the words and generate line.
* Start from the first word.
* | Append the first word.
* | Calculate number of spaces and extra spaces.
* | space -> 1, at least one space
* | extra -> 0,
* | If w moved and w != words.length, meaning not the last line
* | space = remain length / intervals between words + 1(at least 1 space)
* | extra = remain length % intervals between words
* Append the rest of the words. The first one is appended already.
* | Append spaces and extra spaces first. Then append word.
* Deal with padding spaces if it is the last line.
* | If maxWidth > current line length, it's the last line.
* | Pad spaces at the end.
* Add current line to res.
*/
public List<String> fullJustify(String[] words, int maxWidth) {
List<String> res = new LinkedList<>();
for (int i = 0, w; i < words.length; i = w) {
/* Find the number of words to fit this line */
int len = -1; // Length of current line. Init as -1 to remove last space.
for (w = i; w < words.length && len + words[w].length() + 1 <= maxWidth; w++) {
len += words[w].length() + 1; // 1 is an extra space in between words.
}
StringBuilder line = new StringBuilder(words[i]); // Append first word.
/* Calculate number of spaces, number of extra in for each space */
int space = 1; // Number of interval between words. # of words - 1.
int extra = 0; // Extra spaces.
if (w != i + 1 && w != words.length) { // w moved and not last line.
space = (maxWidth - len) / (w - 1 - i) + 1; // w - i - 1 is actually (w - 1) - i + 1 - 1.
extra = (maxWidth - len) % (w - 1 - i); // Extra is the modular.
}
/* Append the rest of the words */
for (int j = i + 1; j < w; j++) {
for (int s = space; s > 0; s--) { // Append space first.
line.append(' ');
}
if (extra-- > 0) {
line.append(' ');
}
line.append(words[j]); // Append the word.
}
/* Deal with last line's padding spaces */
int remain = maxWidth - line.length(); // If not last line, remain will be 0.
while (remain-- > 0) { // Last line.
line.append(' ');
}
res.add(line.toString());
}
return res;
}
@Test
public void testExamples() {
fullJustify(new String[]{"This", "is", "an", "example", "of", "text", "justification."}, 16);
fullJustify(new String[]{""}, 2);
}
}