StringFormatter: 2016 FRQ 4
A step-by-step solution to the 2016 AP CSA FRQ 4 (StringFormatter), covering summing String lengths in a List, computing evenly distributed gap widths, and building a justified string in Java.
Justifying text so it fills an exact width — the same trick a word processor uses — is the whole idea behind this AP Computer Science A free-response question, built out of three small static methods that each handle one piece of the math.
What This FRQ Tests
- AP CSA units: Unit 7 (ArrayList/List) and Unit 4 (Iteration)
- Core skill: accumulating a running total across every element of a
List, then using integer division to spread out leftover space as evenly as possible - Secondary skill: building a
Stringup piece by piece across a loop, reusing already-written helper methods instead of recomputing their logic - Official category: "Array/ArrayList" — the category the modern fixed ordering assigns to FRQ 3, but the 2016 booklet prints this question as FRQ 4. Paired with this same year's FRQ 3 (
Crossword, which tests 2D Array — normally FRQ 4's slot), the two categories are effectively swapped relative to the modern pattern, the same kind of deviation documented for 2018.
The Setup
wordListis aList<String>guaranteed to contain at least two words, letters only.formattedLenis the target total length of the finished string, guaranteed large enough to fit every word and gap.- Total letters — the sum of every word's length. Number of gaps — one fewer than the number of words (gaps sit between words). Basic gap width — as many spaces as can be split evenly across every gap. Leftover spaces — whatever's left after that even split, handed out one at a time to gaps starting from the left.
- Four
staticmethods exist onStringFormatter:totalLetters,basicGapWidth,leftoverSpaces(already implemented, given), andformat. - Three things to write:
totalLetters(part a),basicGapWidth(part b), andformat(part c).
Part (a): Writing totalLetters
The Rule, Broken Down
Add up the length of every word in wordList and return the sum.
Step-by-Step Approach
- Start a running total at
0. - Loop over every index from
0towordList.size() - 1. - Add that word's length to the running total each time.
- Return the total once the loop finishes.
The Code
public static int totalLetters(List<String> wordList)
{
int total = 0;
for (int i = 0; i < wordList.size(); i++)
{
total = total + wordList.get(i).length();
}
return total;
}
Why Each Piece Matters
wordList.get(i).length()chains two Quick Reference methods together:get(i)pulls theStringout of the list, then.length()measures it.- Starting
totalat0is the identity value for addition — it works correctly even for the shortest legalwordList(exactly two words).
Tracing the Example
Using the question's own part-(a) example, ["A", "frog", "is"]:
i |
Word | Length | Running total |
|---|---|---|---|
| 0 | "A" |
1 | 1 |
| 1 | "frog" |
4 | 5 |
| 2 | "is" |
2 | 7 |
The final total, 7, matches the question's stated result exactly. It also checks out against the longer Example 1 later in the question (["AP", "COMP", "SCI", "ROCKS"], lengths 2 + 4 + 3 + 5 = 14), which matches the "Total number of letters in words: 14" given there.
Common Mistakes to Avoid
- Using
<=instead of<in the loop condition — that would callwordList.get(wordList.size()), which is out of bounds. - Confusing
.length()(for aString) with.size()(for theListitself) — mixing these up is common enough that the official 2016 rubric explicitly lists it as a no-penalty slip, but it's still worth keeping straight. - Declaring
totalinside the loop instead of before it — that would reset it back to0on every single iteration instead of accumulating across all of them.
Part (b): Writing basicGapWidth
The Rule, Broken Down
The basic gap width is (formattedLen - totalLetters) / numberOfGaps, where numberOfGaps is one fewer than the number of words. The problem requires calling totalLetters to get that first value.
Step-by-Step Approach
- Call
totalLetters(wordList)to find out how much space the words themselves need. - Subtract that from
formattedLento find out how much space is left for all the gaps combined. - Compute the number of gaps as
wordList.size() - 1. - Divide the leftover space by the number of gaps, and return that.
The Code
public static int basicGapWidth(List<String> wordList, int formattedLen)
{
int spaceForGaps = formattedLen - totalLetters(wordList);
int numberOfGaps = wordList.size() - 1;
return spaceForGaps / numberOfGaps;
}
Why Each Piece Matters
- Calling
totalLetters(wordList)instead of re-summing word lengths by hand is required for full credit here, and avoids keeping two separate copies of the same summing logic. - Integer division (
int / int) in Java truncates toward zero — with both operands here always non-negative, that's exactly "distribute the space evenly, ignore whatever doesn't divide evenly," with no extra rounding logic needed. wordList.size() - 1gaps — with n words lined up in a row, there are always exactly n − 1 spaces between them, the same way a fence with n posts has n − 1 sections.
Tracing the Example
Using all three of the question's own examples (formattedLen = 20 throughout):
| Example | wordList |
Total letters | Gaps | (20 - total) / gaps |
Given basic gap width |
|---|---|---|---|---|---|
| 1 | ["AP","COMP","SCI","ROCKS"] |
14 | 3 | 6 / 3 = 2 |
2 |
| 2 | ["GREEN","EGGS","AND","HAM"] |
15 | 3 | 5 / 3 = 1 |
1 |
| 3 | ["BEACH","BALL"] |
9 | 1 | 11 / 1 = 11 |
11 |
All three match exactly, including Example 2's 5 / 3, which truncates down to 1 rather than rounding to the nearer whole number — that's exactly how the leftover 2 spaces in that example end up handled separately, in part (c).
Common Mistakes to Avoid
- Using
wordList.size()instead ofwordList.size() - 1for the number of gaps — gaps sit between words, so there's always one fewer gap than there are words. - Recomputing the sum of word lengths manually instead of calling
totalLetters(wordList)— this loses credit under the rubric's explicit requirement, and risks a second, possibly inconsistent implementation. - Assuming integer division rounds to the nearest whole number — Example 2 makes clear it truncates instead (
5 / 3is1, not2).
Part (c): Writing format
The Rule, Broken Down
- Every word in
wordListappears in the final string, in order. - Every pair of adjacent words is separated by
basicGapWidthspaces, plus one extra space for each of the leftmostleftoverSpacesgaps. - The problem requires using both
basicGapWidthandleftoverSpaces(the latter already implemented, not something to rewrite).
Step-by-Step Approach
- Call
basicGapWidthonce and save the result. - Call
leftoverSpacesonce and save the result — this count will be used up one at a time as the loop runs. - Start with an empty string.
- Loop over every word except the last one: append the word, then append
basicGapWidthspaces, then — if any leftover spaces remain — append one more space and reduce the leftover count by one. - After the loop, append the final word by itself (it never has a gap following it).
- Return the finished string.
The Code
public static String format(List<String> wordList, int formattedLen)
{
int gapWidth = basicGapWidth(wordList, formattedLen);
int leftovers = leftoverSpaces(wordList, formattedLen);
String formatted = "";
for (int i = 0; i < wordList.size() - 1; i++)
{
formatted = formatted + wordList.get(i);
for (int s = 0; s < gapWidth; s++)
{
formatted = formatted + " ";
}
if (leftovers > 0)
{
formatted = formatted + " ";
leftovers--;
}
}
formatted = formatted + wordList.get(wordList.size() - 1);
return formatted;
}
Why Each Piece Matters
gapWidthandleftoversare each computed once, before the loop, by calling the two already-written helper methods — exactly what the problem requires, and it avoids recalculating either one on every iteration.- The loop only runs through
wordList.size() - 1words — every word except the last — because a gap always comes after a word, and the final word never has one following it. - The inner loop appends exactly
gapWidthspaces every time, guaranteeing the even "basic" distribution happens before any leftover space is ever considered. if (leftovers > 0)paired withleftovers--hands out exactly one extra space per gap, starting from the leftmost gap, and automatically stops the moment every leftover space has been used.- The final word is appended by itself, after the loop — it's the one word that never needs a trailing gap.
Tracing the Example
Walking through Example 2 (["GREEN", "EGGS", "AND", "HAM"], formattedLen = 20), where gapWidth = 1 and leftovers = 2 from part (b):
i |
Word appended | Spaces added | formatted so far |
leftovers after |
|---|---|---|---|---|
| 0 | GREEN |
1 basic + 1 leftover = 2 | "GREEN " |
1 |
| 1 | EGGS |
1 basic + 1 leftover = 2 | "GREEN EGGS " |
0 |
| 2 | AND |
1 basic only (no leftover left) | "GREEN EGGS AND " |
0 |
After the loop, the final word is appended: "GREEN EGGS AND HAM".
Counting that string's 20 characters against the question's own position table (0–19) confirms an exact match: G R E E N at 0–4, two spaces at 5–6, E G G S at 7–10, two spaces at 11–12, A N D at 13–15, one space at 16, H A M at 17–19.
Common Mistakes to Avoid
- Looping through all of
wordList.size()words, then trying to strip a trailing gap off the end afterward — this is much messier than simply stopping one word short in the first place. - Calling
basicGapWidthorleftoverSpacesagain inside the loop — both are meant to be called exactly once, before the loop starts;leftoverSpacesespecially must not be recomputed, since the code is manually counting it down as gaps consume it. - Getting the order of the two space-appending steps backward, or combining them into one calculation — the basic
gapWidthspaces and the possible extra leftover space are two separate, back-to-back appends, not a single combined amount.
Notes: A Cleaner Way to Build the String
StringBuilder is a natural fit for building a string piece by piece inside a loop, and it produces the exact same result:
public static String format(List<String> wordList, int formattedLen)
{
int gapWidth = basicGapWidth(wordList, formattedLen);
int leftovers = leftoverSpaces(wordList, formattedLen);
StringBuilder formatted = new StringBuilder();
for (int i = 0; i < wordList.size() - 1; i++)
{
formatted.append(wordList.get(i));
for (int s = 0; s < gapWidth; s++)
{
formatted.append(" ");
}
if (leftovers > 0)
{
formatted.append(" ");
leftovers--;
}
}
formatted.append(wordList.get(wordList.size() - 1));
return formatted.toString();
}
StringBuilderisn't listed on the Quick Reference sheet at all, but it's completely valid Java and a very common tool for assembling a string across a loop — each.append(...)call modifies the same object in place, instead of the+operator building a brand-newStringevery single time through the loop.- This produces exactly the same result as the version above; the difference is purely style and efficiency, not correctness. Either one earns full credit on the real exam — the only real tradeoff with
StringBuilderis that its methods won't be sitting on the reference sheet to double-check if you're unsure of the exact name.
Key Takeaways
- Splitting a computation into several small
statichelper methods (totalLetters,basicGapWidth,leftoverSpaces,format) and requiring each one to call the others is a common AP CSA pattern — always look for a method already written before recomputing the same value by hand. - Integer division automatically truncates toward zero for two non-negative operands in Java, which is exactly the "even split, ignore the remainder" behavior needed whenever leftover amounts need separate handling afterward.
- Looping through "every element except the last" (stopping at
size() - 1) is the standard shape whenever a value belongs between elements rather than at each one, like a separator or a gap.