CompSci.rocks
FRQcsapa

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 String up 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

  • wordList is a List<String> guaranteed to contain at least two words, letters only.
  • formattedLen is 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 static methods exist on StringFormatter: totalLetters, basicGapWidth, leftoverSpaces (already implemented, given), and format.
  • Three things to write: totalLetters (part a), basicGapWidth (part b), and format (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

  1. Start a running total at 0.
  2. Loop over every index from 0 to wordList.size() - 1.
  3. Add that word's length to the running total each time.
  4. 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 the String out of the list, then .length() measures it.
  • Starting total at 0 is the identity value for addition — it works correctly even for the shortest legal wordList (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 call wordList.get(wordList.size()), which is out of bounds.
  • Confusing .length() (for a String) with .size() (for the List itself) — 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 total inside the loop instead of before it — that would reset it back to 0 on 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

  1. Call totalLetters(wordList) to find out how much space the words themselves need.
  2. Subtract that from formattedLen to find out how much space is left for all the gaps combined.
  3. Compute the number of gaps as wordList.size() - 1.
  4. 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() - 1 gaps — 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 of wordList.size() - 1 for 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 / 3 is 1, not 2).

Part (c): Writing format

The Rule, Broken Down

  1. Every word in wordList appears in the final string, in order.
  2. Every pair of adjacent words is separated by basicGapWidth spaces, plus one extra space for each of the leftmost leftoverSpaces gaps.
  3. The problem requires using both basicGapWidth and leftoverSpaces (the latter already implemented, not something to rewrite).

Step-by-Step Approach

  1. Call basicGapWidth once and save the result.
  2. Call leftoverSpaces once and save the result — this count will be used up one at a time as the loop runs.
  3. Start with an empty string.
  4. Loop over every word except the last one: append the word, then append basicGapWidth spaces, then — if any leftover spaces remain — append one more space and reduce the leftover count by one.
  5. After the loop, append the final word by itself (it never has a gap following it).
  6. 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

  • gapWidth and leftovers are 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() - 1 words — 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 gapWidth spaces every time, guaranteeing the even "basic" distribution happens before any leftover space is ever considered.
  • if (leftovers > 0) paired with leftovers-- 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 (019) 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 basicGapWidth or leftoverSpaces again inside the loop — both are meant to be called exactly once, before the loop starts; leftoverSpaces especially 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 gapWidth spaces 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();
}
  • StringBuilder isn'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-new String every 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 StringBuilder is 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 static helper 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.

Related FRQs