package com.interview.array;
import java.util.ArrayList;
import java.util.List;
/**
* Date 03/12/2016
* @author Tushar Roy
*
* 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.
* You should pack your words in a greedy approach; that is, pack as many words as you can in each line.
*
* Time complexity - O(n) where n is the number of words
* Space complexity - O(1)
*
* https://leetcode.com/problems/text-justification/
*/
public class GreedyTextJustification {
public List<String> fullJustify(String[] words, int maxWidth) {
List<String> result = new ArrayList<>();
for (int i = 0; i < words.length; ) {
int total = words[i].length();
int j = i + 1;
StringBuffer buff = new StringBuffer();
buff.append(words[i]);
while(j < words.length && total + words[j].length() + 1 <= maxWidth) {
total += words[j].length() + 1;
j++;
}
int remaining = maxWidth - total;
//since j is not word length means its not a last line. So pad accordingly.
if (j != words.length) {
int count = j - i - 1;
if (count == 0) {
padSpace(buff, remaining);
} else {
int q = remaining/count;
int r = remaining % count;
for (int k = i + 1; k < j; k++) {
padSpace(buff, q);
if (r > 0) {
buff.append(" ");
r--;
}
buff.append(" ").append(words[k]);
}
}
} else { //if it is last line then left justify all the words.
for (int k = i + 1; k < j; k++) {
buff.append(" ").append(words[k]);
}
padSpace(buff, remaining);
}
result.add(buff.toString());
i = j;
}
return result;
}
private void padSpace(StringBuffer buff, int count) {
for (int i = 0; i < count; i++) {
buff.append(" ");
}
}
public List<String> fullJustify1(String[] words, int maxWidth) {
int currentLength = 0;
int prevIndex = 0;
List<String> result = new ArrayList<>();
for (int i = 0; i < words.length; i++) {
//keep track of length for currentLine. For first word only use length while for remaining words use
//lenght + 1 since there will be a space b/w them.
currentLength += (words[i].length() + (i == prevIndex ? 0 : 1));
//if currentLength exceeds maxWidth it means currentWord cannot in same line.
if (currentLength > maxWidth) {
//subtract current word's length from currentLength
currentLength -= words[i].length() + 1;
StringBuffer builder = new StringBuffer();
//find number of words which will find in the line.
int gaps = i - 1 - prevIndex;
if (gaps > 0) {//if more than one word fits in the gap.
//available number of spaces is below. Subtract gaps because that many spaces have been accounted
//for in currentLength.
int availableSpace = maxWidth - currentLength + gaps;
//first remaining gaps get one extra space.
int remaining = availableSpace % gaps;
//every gap gets this much extra space.
int atleast = availableSpace / gaps;
for (int j = prevIndex; j <= i - 2; j++) {
builder.append(words[j]);
padSpace(builder, atleast);
if (j - prevIndex < remaining) {
padSpace(builder, 1);
}
}
builder.append(words[i - 1]);
} else { //if only one word can fit in a one line then left specify it.
builder.append(words[i - 1]);
padSpace(builder, maxWidth - words[i - 1].length());
}
result.add(builder.toString());
prevIndex = i;
currentLength = words[i].length();
}
}
//handle the last line. Left justify the remaining words
if (prevIndex < words.length) {
StringBuffer builder = new StringBuffer();
int count = 0;
while (prevIndex < words.length) {
builder.append(words[prevIndex]).append(" ");
count += words[prevIndex].length() + 1;
prevIndex++;
}
count--;
//delete extra space added by above for looop.
builder.deleteCharAt(builder.length() - 1);
//whatever spae is left just put it at the end.
padSpace(builder, maxWidth - count);
result.add(builder.toString());
}
return result;
}
public static void main(String args[]) {
String[] input = {"What","must","be","shall","be."};
GreedyTextJustification gtj = new GreedyTextJustification();
List<String> result = gtj.fullJustify(input, 12);
System.out.print(result);
}
}