Scramble: 2014 FRQ 1
A step-by-step solution to the 2014 AP CSA FRQ 1 (scrambleWord/scrambleOrRemove), covering character-by-character string scrambling and removing unchanged entries from a List in Java.
Rearranging letters according to a strict left-to-right scanning rule is the heart of this AP Computer Science A free-response question — first on a single word, then across an entire list of them, where the "unscrambled" leftovers get dropped entirely.
What This FRQ Tests
- AP CSA units: Unit 3 (Boolean Expressions and if Statements), Unit 4 (Iteration), and Unit 7 (ArrayList)
- Core skill: walking through a
Stringone position at a time, deciding whether the current position and the next one form a special pair, and skipping ahead by a different amount depending on that decision - Secondary skill: modifying a
Listin place — replacing some entries and removing others — while keeping the surviving entries in their original relative order - Official category: "Methods and Control Structures" — always FRQ 1 on the AP CSA exam
The Setup
- Both methods belong to the same class, which isn't shown — only the two method signatures matter.
scrambleWord(String word)— takes a word made only of uppercase letters (or an empty string) and returns a scrambled version, following these rules:- Scan the word from left to right, one position at a time.
- Whenever the current letter is
"A"and the very next letter is not"A", swap those two letters in the result. - Once two positions have been swapped, neither one can be swapped again — scanning continues from the position right after the pair.
scrambleOrRemove(List<String> wordList)— replaces every entry inwordListwith its scrambled version, then removes any entry that came back identical to what it started as. The relative order of whatever survives stays the same.
Part (a): Writing scrambleWord
The Rule, Broken Down
- Look at the word two letters at a time, starting from the very first letter.
- If the current letter is
"A"and the next one is not, those two letters swap places in the output. - If a swap just happened, the next comparison starts after both swapped letters — never re-examine a letter that was just involved in a swap.
- If the current letter isn't
"A", or there's no next letter to compare against, or the next letter is also"A", it's copied through unchanged, and scanning moves ahead by just one position.
Step-by-Step Approach
- Build the result one piece at a time in a
Stringvariable, starting empty. - Track a position
i, starting at0. - While
iis still a valid index intoword:- Check whether
word's letter atiis"A", there's a letter ati + 1, and that next letter is not"A". - If all three are true, append the letter at
i + 1followed by the letter ati, then jumpiahead by2. - Otherwise, append just the letter at
i, then moveiahead by1.
- Check whether
- Once
ireaches the end of the word, return what's been built.
The Code
public static String scrambleWord(String word)
{
String result = "";
int i = 0;
while (i < word.length())
{
if (word.substring(i, i + 1).equals("A") && i + 1 < word.length()
&& !word.substring(i + 1, i + 2).equals("A"))
{
result = result + word.substring(i + 1, i + 2) + word.substring(i, i + 1);
i = i + 2;
}
else
{
result = result + word.substring(i, i + 1);
i = i + 1;
}
}
return result;
}
Why Each Piece Matters
word.substring(i, i + 1)— pulls out a single character as a one-characterString.charAtwould grab it more directly, but it isn't on the Quick Reference sheet, so this solution sticks withsubstringto stay within what's guaranteed to be there for you to look up.i + 1 < word.length()— this check has to come before looking atword.substring(i + 1, i + 2). Skipping it would try to read one character past the end of the word on the very last letter, throwing an exception.i = i + 2after a swap,i = i + 1otherwise — this is what enforces "once swapped, neither position can be involved in a future swap." Advancing by only1after a swap would let the just-swapped letter be re-examined.- Building
resultwith+, not modifyingword—Strings in Java are immutable, so the originalwordis never changed; a brand-newStringis assembled and returned instead.
Tracing the Example
Walking through "ABRACADABRA" position by position (11 letters, indices 0–10):
i |
Letter at i |
Next letter | Swap? | Appended | Next i |
|---|---|---|---|---|---|
| 0 | A | B | yes | B, A |
2 |
| 2 | R | A | no | R |
3 |
| 3 | A | C | yes | C, A |
5 |
| 5 | A | D | yes | D, A |
7 |
| 7 | A | B | yes | B, A |
9 |
| 9 | R | A | no | R |
10 |
| 10 | A | (none) | no | A |
11 |
Concatenating every "Appended" column top to bottom gives "BARCADABARA" — matching the table's expected result exactly.
A couple of the shorter examples highlight the edge cases:
"AARDVARK": ati = 0the letter is"A", but the next letter (i = 1) is also"A"— so no swap happens there, and scanning only moves ahead by1. The very next check, ati = 1, does find"A"followed by"R"and swaps. Final result:"ARADVRAK", matching the table."A"and"": with only zero or one letters,i + 1 < word.length()is never true, so no swap is ever possible — both are returned unchanged, exactly as the table shows.
Common Mistakes to Avoid
- Checking
word.substring(i + 1, i + 2)before confirmingi + 1 < word.length(). On the last letter of the word, this throws aStringIndexOutOfBoundsException. - Advancing
iby only1after a swap. This lets the second letter of a just-completed swap be examined again, which can trigger an extra, unintended swap and produce the wrong result on words like"AARDVARK". - Swapping when the next letter is also
"A". The rule specifically requires the second letter to be something other than"A"— twoAs in a row are left alone. - Forgetting the empty-string and single-letter cases. Both are valid input per the precondition and should simply return unchanged, which falls out naturally as long as the bounds check is written correctly.
Part (b): Writing scrambleOrRemove
The Rule, Broken Down
- Every entry in
wordListgets replaced by callingscrambleWordon it. - If an entry's scrambled version is identical to what it started as, that entry is removed entirely instead of being replaced.
- Whatever entries remain keep the same relative order they had before the call.
Step-by-Step Approach
- Track a position
i, starting at0— not a simple for-loop, since removing an entry changes where the next entry to check ends up. - While
iis still a valid index intowordList:- Grab the entry currently at position
i, and compute its scrambled version by callingscrambleWordon it. - If the scrambled version equals the original, remove the entry at
i— and leaveiwhere it is, since the next entry has now slid into that same position. - Otherwise, replace the entry at
iwith its scrambled version, then moveiahead by1.
- Grab the entry currently at position
- Once
ireaches the end of the (possibly shrunken) list, the method is done — there's nothing to return.
The Code
public static void scrambleOrRemove(List<String> wordList)
{
int i = 0;
while (i < wordList.size())
{
String original = wordList.get(i);
String scrambled = scrambleWord(original);
if (scrambled.equals(original))
{
wordList.remove(i);
}
else
{
wordList.set(i, scrambled);
i++;
}
}
}
Why Each Piece Matters
- Comparing
scrambledtooriginalbefore overwriting anything — oncewordList.set(i, scrambled)runs, the original value is gone. The comparison has to happen while both versions are still available. idoesn't advance after aremove—remove(index)shifts every later element one position to the left, so the entry that used to be ati + 1is now sitting ati. Advancingianyway would skip over it without ever checking it.- A
whileloop instead of aforloop — aforloop'si++runs unconditionally every iteration, which is exactly wrong here: after a removal,ineeds to stay put, not increase. wordList.size()re-checked every iteration — since removals shrink the list, the loop bound can't be captured once at the start; it has to be re-evaluated as written in thewhilecondition.
Tracing the Example
Starting list (5 entries): "TAN", "ABRACADABRA", "WHOA", "APPLE", "EGGS".
i |
Entry checked | Scrambled version | Unchanged? | Action | List afterward |
|---|---|---|---|---|---|
| 0 | "TAN" |
"TNA" |
no | replace, i → 1 |
TNA, ABRACADABRA, WHOA, APPLE, EGGS |
| 1 | "ABRACADABRA" |
"BARCADABARA" |
no | replace, i → 2 |
TNA, BARCADABARA, WHOA, APPLE, EGGS |
| 2 | "WHOA" |
"WHOA" |
yes | remove, i stays 2 |
TNA, BARCADABARA, APPLE, EGGS |
| 2 | "APPLE" |
"PAPLE" |
no | replace, i → 3 |
TNA, BARCADABARA, PAPLE, EGGS |
| 3 | "EGGS" |
"EGGS" |
yes | remove, i stays 3 |
TNA, BARCADABARA, PAPLE |
At this point i = 3 and the list's size has shrunk to 3, so the loop ends. Final list: "TNA", "BARCADABARA", "PAPLE" — matching the "After the call" table exactly, both in contents and order.
Common Mistakes to Avoid
- Using a
for (int i = 0; i < wordList.size(); i++)loop. This is the single most common bug in this kind of problem: after aremove, the unconditionali++skips checking the element that just slid into positioni. - Capturing
wordList.size()in a variable before the loop starts. Since removals shrink the list, using a stale, pre-computed size either loops too many times (crashing with an index error) or stops too early. - Comparing against the original list entry after already overwriting it with
set. Save the original value in a variable first, exactly asoriginaldoes here. - Removing by value instead of by index (e.g. some equivalent of
wordList.remove(original)).List'sremove(int index)and a value-basedremove(Object)are different overloads entirely — this solution needs the index-based one.
Notes: A Method Not on the AP CSA Quick Reference Sheet
charAt reads more naturally than pulling out a one-character substring every time, and it's a common thing to reach for once you're used to it:
public static String scrambleWord(String word)
{
String result = "";
int i = 0;
while (i < word.length())
{
if (word.charAt(i) == 'A' && i + 1 < word.length() && word.charAt(i + 1) != 'A')
{
result = result + word.charAt(i + 1) + word.charAt(i);
i = i + 2;
}
else
{
result = result + word.charAt(i);
i = i + 1;
}
}
return result;
}
word.charAt(i)returns the individualcharat a position directly, instead of a one-characterStringproduced bysubstring(i, i + 1).- Comparing
chars uses==and!=(comparing primitive values), which is different from comparingStrings with.equals()— that's a completely normal and correct thing to do here, not a mixup. charAtisn't listed on the real exam's Java Quick Reference sheet, but that doesn't make it off-limits — AP CSA graders accept any correct Java. The only actual tradeoff is not having its exact behavior available to look up on the reference sheet if you second-guess yourself mid-exam.
Key Takeaways
- A scan that sometimes consumes one element and sometimes consumes two needs a
whileloop with a manually-advanced index — aforloop's fixedi++can't express "advance by a different amount depending on what just happened." - Removing from a
Listwhile iterating over it is one of the most common sources of subtle bugs in AP CSA: only advance the loop index when nothing was removed, since a removal shifts every later element down by one. - Save any value you'll need to compare before overwriting it — once
set(or a plain reassignment) runs, the original is gone for good.