I have to read in an integer which will be the length of the succeeding lines. (The lines of text will never be longer than the length provided).
I then have to read
I followed Shahroz Saleem's answer (but my rep is too low to comment :/) - however, I needed one minor change as it does not take into account words longer than the line length (such as URL's in the text.)
import java.util.ArrayList;
import java.util.List;
public class Utils {
public static List<String> fullJustify(String words, int maxWidth) {
return fullJustify(words.split(" "), maxWidth);
}
public static List<String> fullJustify(String[] words, int maxWidth) {
int n = words.length;
List<String> justifiedText = new ArrayList<>();
int currLineIndex = 0;
int nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
while (currLineIndex < n) {
StringBuilder line = new StringBuilder();
for (int i = currLineIndex; i < nextLineIndex; i++) {
line.append(words[i] + " ");
}
currLineIndex = nextLineIndex;
nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
justifiedText.add(line.toString());
}
for (int i = 0; i < justifiedText.size() - 1; i++) {
String fullJustifiedLine = getFullJustifiedString(justifiedText.get(i).trim(), maxWidth);
justifiedText.remove(i);
justifiedText.add(i, fullJustifiedLine);
}
String leftJustifiedLine = getLeftJustifiedLine(justifiedText.get(justifiedText.size() - 1).trim(), maxWidth);
justifiedText.remove(justifiedText.size() - 1);
justifiedText.add(leftJustifiedLine);
return justifiedText;
}
public static int getNextLineIndex(int currLineIndex, int maxWidth, String[] words) {
int n = words.length;
int width = 0;
int count = 0;
while (currLineIndex < n && width < maxWidth) {
width += words[currLineIndex++].length() + 1;
count++;
}
if (width > maxWidth + 1 && count > 1)
currLineIndex--;
return currLineIndex;
}
public static String getFullJustifiedString(String line, int maxWidth) {
StringBuilder justifiedLine = new StringBuilder();
String[] words = line.split(" ");
int occupiedCharLength = 0;
for (String word : words) {
occupiedCharLength += word.length();
}
int remainingSpace = maxWidth - occupiedCharLength;
int spaceForEachWordSeparation = words.length > 1 ? remainingSpace / (words.length - 1) : remainingSpace;
int extraSpace = remainingSpace - spaceForEachWordSeparation * (words.length - 1);
for (int j = 0; j < words.length - 1; j++) {
justifiedLine.append(words[j]);
for (int i = 0; i < spaceForEachWordSeparation; i++)
justifiedLine.append(" ");
if (extraSpace > 0) {
justifiedLine.append(" ");
extraSpace--;
}
}
justifiedLine.append(words[words.length - 1]);
for (int i = 0; i < extraSpace; i++)
justifiedLine.append(" ");
return justifiedLine.toString();
}
public static String getLeftJustifiedLine(String line, int maxWidth) {
int lineWidth = line.length();
StringBuilder justifiedLine = new StringBuilder(line);
//for (int i = 0; i < maxWidth - lineWidth; i++)
// justifiedLine.append(" ");
return justifiedLine.toString();
}
}
Note I also commented out the spaces padding for the last line of each paragraph (in getLeftJustifiedLine) and made the methods static..
The First part of this presentation contains a Dynamic Programming Algorithm for Justification of Text.
You just need to call fullJustify()
method where in list of words needs to be passed along with the max width of each line you want in output.
public List<String> fullJustify(String[] words, int maxWidth) {
int n = words.length;
List<String> justifiedText = new ArrayList<>();
int currLineIndex = 0;
int nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
while (currLineIndex < n) {
StringBuilder line = new StringBuilder();
for (int i = currLineIndex; i < nextLineIndex; i++) {
line.append(words[i] + " ");
}
currLineIndex = nextLineIndex;
nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
justifiedText.add(line.toString());
}
for (int i = 0; i < justifiedText.size() - 1; i++) {
String fullJustifiedLine = getFullJustifiedString(justifiedText.get(i).trim(), maxWidth);
justifiedText.remove(i);
justifiedText.add(i, fullJustifiedLine);
}
String leftJustifiedLine = getLeftJustifiedLine(justifiedText.get(justifiedText.size() - 1).trim(), maxWidth);
justifiedText.remove(justifiedText.size() - 1);
justifiedText.add(leftJustifiedLine);
return justifiedText;
}
public static int getNextLineIndex(int currLineIndex, int maxWidth, String[] words) {
int n = words.length;
int width = 0;
while (currLineIndex < n && width < maxWidth) {
width += words[currLineIndex++].length() + 1;
}
if (width > maxWidth + 1)
currLineIndex--;
return currLineIndex;
}
public String getFullJustifiedString(String line, int maxWidth) {
StringBuilder justifiedLine = new StringBuilder();
String[] words = line.split(" ");
int occupiedCharLength = 0;
for (String word : words) {
occupiedCharLength += word.length();
}
int remainingSpace = maxWidth - occupiedCharLength;
int spaceForEachWordSeparation = words.length > 1 ? remainingSpace / (words.length - 1) : remainingSpace;
int extraSpace = remainingSpace - spaceForEachWordSeparation * (words.length - 1);
for (int j = 0; j < words.length - 1; j++) {
justifiedLine.append(words[j]);
for (int i = 0; i < spaceForEachWordSeparation; i++)
justifiedLine.append(" ");
if (extraSpace > 0) {
justifiedLine.append(" ");
extraSpace--;
}
}
justifiedLine.append(words[words.length - 1]);
for (int i = 0; i < extraSpace; i++)
justifiedLine.append(" ");
return justifiedLine.toString();
}
public String getLeftJustifiedLine(String line, int maxWidth) {
int lineWidth = line.length();
StringBuilder justifiedLine = new StringBuilder(line);
for (int i = 0; i < maxWidth - lineWidth; i++)
justifiedLine.append(" ");
return justifiedLine.toString();
}
Below is the sample conversion where maxWidth was 80 characters: The following paragraph contains 115 words exactly and it took 55 ms to write the converted text to external file.
I've tested this code for a paragraph of about 70k+ words and it took approx 400 ms to write the converted text to a file.
Input
These features tend to make legal writing formal. This formality can take the form of long sentences, complex constructions, archaic and hyper-formal vocabulary, and a focus on content to the exclusion of reader needs. Some of this formality in legal writing is necessary and desirable, given the importance of some legal documents and the seriousness of the circumstances in which some legal documents are used. Yet not all formality in legal writing is justified. To the extent that formality produces opacity and imprecision, it is undesirable. To the extent that formality hinders reader comprehension, it is less desirable. In particular, when legal content must be conveyed to nonlawyers, formality should give way to clear communication.
Output
These features tend to make legal writing formal. This formality can take the
form of long sentences, complex constructions, archaic and hyper-formal
vocabulary, and a focus on content to the exclusion of reader needs. Some of
this formality in legal writing is necessary and desirable, given the importance
of some legal documents and the seriousness of the circumstances in which some
legal documents are used. Yet not all formality in legal writing is justified.
To the extent that formality produces opacity and imprecision, it is
undesirable. To the extent that formality hinders reader comprehension, it is
less desirable. In particular, when legal content must be conveyed to
nonlawyers, formality should give way to clear communication.
The way I would go about this is to use a loop with regular-expression replacements.
After you do the initial replacement, I'd suggest you use a while loop, bounded on the length of the string being less than the target size. Initialize int numUnderscores = 1;
outside of the while. Then the steps inside the loop will be:
"/[^_](_{" + numUnderscores + "})[^_]/"
which says "any char that is not an underscore, followed by numUnderscores instances of the underscore char, followed by any char that is not an underscore".ReplaceFirst()
to perform the replacementnumUnderscores
Obviously, since this is a homework problem, I'm leaving the actual process of writing the code as an exercise. If you have specific questions about some piece of it, or about some component of the logic structure I described, just ask in comments!
The benefit of doing things this way is that it will work for any size string, and is very configurable for different situations.
The hardest thing about this problem is defining "as evenly as possible".
Your example:
Hello__this__is__a_test_string
... makes all the longer gaps be at the left. Wouldn't:
Hello__this_is__a_test__string
... fit the imprecise description of the problem better, with the longer gaps spread evenly through the output string?
However, let's solve it so it gives the sample answer.
numNewChars
== lengthWanted
minus inputString.length()
numGaps
-- it's the number of words minus one.n
or n+1
new spaces. n
is numNewChars / numGaps
-- integer division; rounds down.n+1
new spaces instead of n
? It's the remainder: plusOnes = numNewChars % numGaps
That's all the numbers you need. Now using whatever method you've been taught (since this is evidently a homework problem, you don't want to use language features or libraries that haven't been covered in your lessons), go through the string:
plusOnes
spaces, insert n+1
spaces, in addition to the space that's already there.n
spaces.One very basic method would be as follows:
String output= "";
for(int i=0; i<input.length(); i++) {
char c = input.charAt(i);
if(c == ' ' {
output += ...; // appropriate number of "_" chars
} else {
output += "" + c; // "" + just turns the char into a String.
}
}
Let's try to break the problem down:
Subtract the length of the string from 30 - that's the number of extra spaces you'll be adding somewhere (3 in this case).
Count the number of existing spaces (5 in this case).
Now you know that you need to distribute that first number of extra spaces into the existing spaces as evenly as possible (in this case, distribute 3 into 5).
Think about how you would distribute something like this in real life, say balls into buckets. You would probably rotate through your buckets, dropping a ball in each one until you ran out. So consider how you might achieve this in your java code (hint: look at the different kinds of loops).