Contents and Exam-Wide Approach

Purpose of This Guide

The guide translates each prompt into a precise algorithm, develops the Java solution one decision at a time, dry-runs the code on the examples, and explains why the result satisfies every stated precondition and postcondition.

Original guide note: Based on the uploaded paper: ap26-frq-computer-science-a.pdf.

Contents from the uploaded guide
SectionWhat is solvedSource pages
Question 1Account constructor; remove each hyphen and the character before it3-5
Question 2Write the complete Bottle class and maintain object state6-7
Question 3Match student IDs across two ArrayLists and compare absences8-10
Question 4Traverse one row of a 2D array; sum points and test colors11-13
Final answer sheetAll exam-ready Java responses in one placeThis guide

A Reliable AP CSA Solving Routine

  1. Identify exactly what must be returned or assigned. Do not add behavior the prompt does not request.
  2. List the state that must persist between method calls. Persistent state belongs in instance variables; temporary state belongs in local variables.
  3. Translate each English condition into a Boolean expression, paying attention to words such as less than, both, first, and unchanged.
  4. Choose a loop whose progress is obvious. State what changes on every iteration so the loop cannot stall.
  5. Dry-run the most important example and at least one boundary case before finalizing the code.
Java rules that matter repeatedlyCompare String contents with .equals, not ==. Respect the supplied method signatures. Trust the stated preconditions, so do not waste time validating inputs that the prompt guarantees are valid. Do not modify a list or field when the postcondition says it must remain unchanged.

Solution Conventions

  • Code is formatted to compile in ordinary Java, assuming the question-provided classes and hidden methods exist.
  • A method can be written in many equivalent ways. The guide favors direct, readable solutions with minimal moving parts.
  • Complexity notes are included for understanding, even though the prompts do not ask for Big-O analysis.
Question 1Candidate search and a left-to-right String scan.
Question 2Persistent object state and a strict threshold.
Question 3Nested ArrayList matching with .equals.
Question 4One-row 2D-array traversal, accumulation, and a color test.

Question 1 - Account

Part A: Constructor That Finds the First Available Username

Task in one sentenceUse requestedName itself if it is available; otherwise test requestedName1, requestedName2, requestedName3, and so on, stopping at the first available value and storing it in username.
InputString requestedName, the original base username.
Required effectAssign the first available candidate to the instance variable username. A constructor has no return value.

Step 1: Decide what changes and what stays fixed

  • requestedName never changes. Every numbered candidate must be built from this original base name.
  • candidate is the username currently being tested.
  • suffix is the next integer that will be appended if candidate is unavailable.

Step 2: Establish the loop order

  1. Start candidate as requestedName so the unmodified name is checked first.
  2. Start suffix at 1 because the first variation must end in 1.
  3. While candidate is unavailable, replace it with requestedName + suffix and then advance suffix.
  4. When the loop ends, candidate is the first available username; assign it to the instance variable.

Exam-ready constructor

Java - Question 1A
public Account(String requestedName)
{
  String candidate = requestedName;
  int suffix = 1;
  while (!isAvailable(candidate))
  {
    candidate = requestedName + suffix;
    suffix++;
  }
  username = candidate;
}
Why the increment comes after candidate creationWhen suffix is 1, the code must create requestedName1. Incrementing suffix after that assignment makes 2 ready for the next iteration without skipping any integer.

Question 1A - Dry Run and Correctness

Example trace: requestedName is "Luis-Cruz"

Assume "Luis-Cruz", "Luis-Cruz1", and "Luis-Cruz2" are unavailable, while "Luis-Cruz3" is available.

Constructor dry run
Loop testcandidate testedAvailable?Action
1Luis-CruzNoCreate Luis-Cruz1; suffix becomes 2
2Luis-Cruz1NoCreate Luis-Cruz2; suffix becomes 3
3Luis-Cruz2NoCreate Luis-Cruz3; suffix becomes 4
4Luis-Cruz3YesExit loop; assign username = "Luis-Cruz3"

Zero-iteration case

If "PSmith" is available, the while condition is false immediately. No number is appended, and username is assigned "PSmith". This is exactly the required behavior.

Why this constructor is correct

  • It calls isAvailable on every candidate that is considered, including the original requested name.
  • The generated sequence is requestedName, requestedName1, requestedName2, ... with no gaps.
  • The loop stops only when the current candidate is available, so the assigned value is valid.
  • Because candidates are tested in increasing suffix order, the first available variation is selected.

Common mistakes to avoid

MistakeWhy it fails
Building candidate = candidate + suffixProduces names such as Name1, then Name12, instead of Name2.
Starting suffix at 0Tests Name0 even though the prompt says to begin with 1.
Checking only numbered versionsIncorrectly ignores the possibility that requestedName itself is available.
Forgetting to assign usernameThe local candidate is correct, but the Account object remains uninitialized.
Final answerThe constructor above checks the original request first, tests numbered candidates without gaps, and stores the first available candidate in username.

Part B: getShortenedName

Task in one sentenceReturn a new String in which every hyphen and the character immediately before that hyphen are omitted, while leaving username unchanged.
InputNo parameter. The method reads the instance variable username.
Output and postconditionReturn the shortened String; do not assign a new value to username.

Step 1: Reframe the deletion rule

Each hyphen defines a two-character pair to remove: the character immediately before it and the hyphen itself. The preconditions guarantee that a hyphen is never first, never last, and never adjacent to another hyphen, so every removal pair is well-defined.

Step 2: Scan without modifying username

  1. Maintain index, the position currently being considered, and shortened, the output built so far.
  2. If the character after index is a hyphen, then the current character is the character that must be removed. Skip both positions by adding 2 to index.
  3. Otherwise copy the current character into shortened and advance index by 1.
  4. Return shortened after every position has been handled.

Exam-ready method

Java - Question 1B
public String getShortenedName()
{
  String shortened = "";
  int index = 0;
  while (index < username.length())
  {
    if (index < username.length() - 1
        && username.substring(index + 1, index + 2).equals("-"))
    {
      index += 2;
    }
    else
    {
      shortened += username.substring(index, index + 1);
      index++;
    }
  }
  return shortened;
}
Postcondition checkThe method reads username but never assigns a new value to it. Since String objects are immutable and all output is accumulated in the local variable shortened, username is unchanged.

Question 1B - Dry Run and Edge Cases

Trace for username = "Amy-Marie-Lin"

Left-to-right String scan
indexRelevant charactersDecisionshortened after action
0A followed by mCopy AA
1m followed by yCopy mAm
2y followed by -Skip y and -; jump to index 4Am
4-7M, a, r, iCopy each characterAmMari
8e followed by -Skip e and -; jump to index 10AmMari
10-12L, i, nCopy each characterAmMariLin

Returned value: "AmMariLin"

No-hyphen case

For username = "SammyB3", the look-ahead condition is never true. Every character is copied, so the returned value is "SammyB3".

Boundary observations

  • The condition index < username.length() - 1 prevents substring(index + 1, index + 2) from going past the end of the String.
  • A final ordinary character is copied normally because there is no following hyphen.
  • For a name such as "A-B-C", the method skips A-, then B-, and returns "C".

Common mistakes to avoid

MistakeResult
Removing only the hyphenLeaves the character that was supposed to be deleted.
Changing username directlyViolates the postcondition that username must be unchanged.
Advancing index by only 1 after a pair is foundThe hyphen is processed again or copied incorrectly.
Using a substring endpoint beyond username.length()Causes StringIndexOutOfBoundsException.
Final answerThe method returns "AmMariLin" for "Amy-Marie-Lin", returns an unchanged copy for a no-hyphen name, safely skips every required two-character pair, and never modifies username.

Question 2 - Complete Bottle Class

Task in one sentenceA Bottle begins full, loses a requested amount on each valid call, refills to capacity whenever the new amount is strictly below 25% of capacity, and returns the amount remaining after that rule is applied.
Constructor inputdouble bottleCapacity, used as both the permanent capacity and initial amount.
Method input and outputupdateAmount receives double amountRemoved, updates persistent state, and returns the final double amount.

Step 1: Choose the instance variables

Persistent Bottle state
FieldMeaningWhy it must be stored
capacityMaximum and refill amountNeeded for the 25% threshold and for resetting the bottle.
amountCurrent amount of liquidMust persist and change across multiple updateAmount calls.

Step 2: Translate the constructor

The constructor receives the capacity. The prompt says every new bottle is filled to capacity, so both fields initially receive the same value.

Step 3: Translate updateAmount in the required order

  1. Subtract amountRemoved from the current amount.
  2. Compare the new amount with 0.25 * capacity.
  3. If the new amount is strictly less than that threshold, reset amount to capacity.
  4. Return the final amount.

Complete class

Java - Question 2
public class Bottle
{
  private double capacity;
  private double amount;

  public Bottle(double bottleCapacity)
  {
    capacity = bottleCapacity;
    amount = bottleCapacity;
  }

  public double updateAmount(double amountRemoved)
  {
    amount -= amountRemoved;
    if (amount < 0.25 * capacity)
    {
      amount = capacity;
    }
    return amount;
  }
}

Question 2 - Verification and Boundary Cases

Trace for the 1,000 mL water bottle

Successive method calls preserve and update object state
CallAmount after subtraction25% thresholdRefill?Returned
updateAmount(400.0)600.0250.0No600.0
updateAmount(100.0)500.0250.0No500.0
updateAmount(300.0)200.0250.0Yes -> 1000.01000.0

Trace for the 40 mL shampoo bottle

Exactly 25% versus strictly below 25%
CallAmount after subtractionThresholdReasonReturned
updateAmount(30.0)10.010.0Equal to 25%, not below it10.0
updateAmount(1.0)9.010.0Below 25%, so reset40.0
Critical inequalityThe prompt says less than 25%, so the comparison must be amount < 0.25 * capacity. Using <= would incorrectly refill the 40 mL bottle when exactly 10 mL remains.

Why the class is correct

  • capacity remains available for every future call and is never overwritten.
  • amount is updated cumulatively, so each call begins with the amount left by the previous call.
  • The refill test occurs after removal, matching the wording and examples.
  • The precondition guarantees amountRemoved is positive and no greater than the current amount, so no extra validation is required.

Common mistakes to avoid

MistakeWhy it fails
Storing only amountAfter several removals, the original capacity is no longer known for refilling.
Testing before subtractingUses the old amount instead of the amount remaining after use.
Using <= 25%Refills at exactly 25%, contradicting the sample.
Returning before applying the refill ruleReturns the temporary low amount instead of the reset amount.
Final answerThe complete Bottle class above stores both required fields, starts full, subtracts before testing, refills only below 25%, and returns the post-rule amount.

Question 3 - Attendance

Task in one sentenceCount each student whose ID appears in both lists and whose history-course absence count is greater than that same student's math-course absence count.
Inputs available to the methodThe instance variables historyList and mathList, whose elements are CourseRecord objects.
OutputAn int count. Neither list may be changed.

Step 1: Recognize the matching problem

A CourseRecord in historyList cannot be compared with just any CourseRecord in mathList. The records must first be matched by student ID. Only after the IDs match should the two absence counts be compared.

Step 2: Use nested traversal

  1. Visit each history record.
  2. Search mathList for a record with the same student ID.
  3. When a match is found, compare the absence counts and increment count only when history is greater.
  4. Break out of the inner loop after the matching ID is processed. The preconditions guarantee no duplicate ID in mathList.

Exam-ready method

Java - Question 3
public int moreHistoryThanMathAbsences()
{
  int count = 0;
  for (CourseRecord historyRecord : historyList)
  {
    for (CourseRecord mathRecord : mathList)
    {
      if (historyRecord.getStudentID().equals(
          mathRecord.getStudentID()))
      {
        if (historyRecord.getAbsences()
            > mathRecord.getAbsences())
        {
          count++;
        }
        break;
      }
    }
  }
  return count;
}
String comparisongetStudentID returns a String. Use .equals to compare the character sequences. The == operator would test whether the two variables refer to the same String object, which is not the requirement.

Question 3 - Dry Run and Proof of Correct Counting

Students appearing in both lists

Matched attendance records
Student IDHistory absencesMath absencesHistory > Math?Counted?
dr0322No - equalNo
ot3221YesYes
sq9831YesYes
ry0013NoNo

Final count: 2

The history-only IDs "rc29" and "br98" are ignored because no matching math record exists. The math-only IDs are never selected by the outer loop and therefore also do not affect the count.

Why the method counts exactly the correct students

  • Every historyList record is examined once by the outer loop.
  • For that record, every mathList record is considered until the matching ID is found or the list ends.
  • count increases only after both required conditions are true: matching IDs and greater history absences.
  • Because IDs are unique within each list, a student can increase count at most once.
  • The method only calls accessor methods; it never adds, removes, reorders, or replaces list elements, so both lists remain unchanged.

Efficiency

If H is the size of historyList and M is the size of mathList, the worst-case running time is O(H x M), and the extra space is O(1). This direct approach is fully appropriate for the information and tools supplied in the prompt.

Common mistakes to avoid

MistakeConsequence
Comparing records at the same list indexThe lists can be in different orders and have different students.
Using == for student IDsMay fail even when two IDs contain the same characters.
Counting every history record with many absencesIgnores the requirement that the student be enrolled in both courses.
Using >= instead of >Incorrectly counts students with equal absence totals, such as dr03.
Final answermoreHistoryThanMathAbsences returns 2 for the sample data and correctly counts only matched student IDs with strictly more history absences.

Question 4 - GameBoard

Task in one sentenceSum the point values in targetRow; return that sum normally, but return twice the sum when every Space in the row has the same color.
Inputint targetRow, the index of the row to process in board.
OutputThe row's point sum, doubled only if every color in that row matches.

Step 1: Track the two facts needed

Local variables used during the row traversal
Local variablePurpose
sumAccumulates the point values of all spaces in targetRow.
firstColorProvides the reference color against which every space is compared.
allSameColorRemains true unless a space with a different color is found.

Step 2: Traverse only the requested row

  1. Initialize firstColor from board[targetRow][0]. The preconditions guarantee the row has elements and no element is null.
  2. Loop across the columns in board[targetRow].
  3. Add each Space's points to sum.
  4. Compare each Space's color with firstColor using .equals. If any differs, set allSameColor to false.
  5. After the loop, return 2 * sum when allSameColor is true; otherwise return sum.

Exam-ready method

Java - Question 4
public int getPointsForRow(int targetRow)
{
  int sum = 0;
  String firstColor = board[targetRow][0].getColor();
  boolean allSameColor = true;
  for (int col = 0; col < board[targetRow].length; col++)
  {
    Space current = board[targetRow][col];
    sum += current.getPoints();
    if (!current.getColor().equals(firstColor))
    {
      allSameColor = false;
    }
  }
  if (allSameColor)
  {
    return 2 * sum;
  }
  return sum;
}

Question 4 - Dry Run and Correctness

Row 0 from the sample board

Points: 100 + 100 + 500 + 500 + 100 = 1300. The colors are orange, red, blue, green, and red, so allSameColor becomes false. The method returns 1300.

Row 2 from the sample board

Points: 200 + 300 + 100 + 200 + 200 = 1000. Every Space is red, so allSameColor remains true. The method returns 2 * 1000 = 2000.

Sample board results
RowSumColor testMultiplierReturned value
01300Not all equal11300
21000All red22000

Why the method is correct

  • Every element in targetRow contributes exactly once to sum.
  • allSameColor starts true and can only change to false, so one mismatch is enough to record that the row is mixed.
  • If no mismatch is found, every color equals firstColor and the sum is doubled exactly once.
  • The method uses board[targetRow].length, so it traverses the actual requested row rather than assuming all rows have the same length.

Efficiency

For C columns in targetRow, the running time is O(C) and the extra space is O(1).

Common mistakes to avoid

MistakeWhy it is wrong
Checking only adjacent color pairs incompletelyCan miss a later mismatch unless every element is tested.
Doubling points as they are addedThe multiplier depends on the entire row and should be applied after the color test is complete.
Using board.length as the column limitboard.length is the number of rows, not the number of columns in targetRow.
Using == to compare colorsTests String identity instead of String contents.
Final answerThe method returns 1300 for sample row 0 and 2000 for sample row 2. It sums every space once and doubles only after confirming that every color matches.

Final Answer Sheet - Questions 1-4

This section contains only the code a student could enter for the requested responses. Explanations and traces appear in the preceding sections.

Consolidated Exam-Ready Answer Sheet - Questions 1 and 2

Question 1A - Account constructor

Java - Final answer 1A
public Account(String requestedName)
{
  String candidate = requestedName;
  int suffix = 1;
  while (!isAvailable(candidate))
  {
    candidate = requestedName + suffix;
    suffix++;
  }
  username = candidate;
}

Question 1B - getShortenedName

Java - Final answer 1B
public String getShortenedName()
{
  String shortened = "";
  int index = 0;
  while (index < username.length())
  {
    if (index < username.length() - 1
        && username.substring(index + 1, index + 2).equals("-"))
    {
      index += 2;
    }
    else
    {
      shortened += username.substring(index, index + 1);
      index++;
    }
  }
  return shortened;
}

Consolidated Answer Sheet - Question 2

Complete Bottle class

Java - Final answer 2
public class Bottle
{
  private double capacity;
  private double amount;

  public Bottle(double bottleCapacity)
  {
    capacity = bottleCapacity;
    amount = bottleCapacity;
  }

  public double updateAmount(double amountRemoved)
  {
    amount -= amountRemoved;
    if (amount < 0.25 * capacity)
    {
      amount = capacity;
    }
    return amount;
  }
}
One-line boundary reminderExactly 25% remaining does not trigger a refill; only an amount strictly below 25% does.

Consolidated Exam-Ready Answer Sheet - Questions 3 and 4

Question 3 - moreHistoryThanMathAbsences

Java - Final answer 3
public int moreHistoryThanMathAbsences()
{
  int count = 0;
  for (CourseRecord historyRecord : historyList)
  {
    for (CourseRecord mathRecord : mathList)
    {
      if (historyRecord.getStudentID().equals(
          mathRecord.getStudentID()))
      {
        if (historyRecord.getAbsences()
            > mathRecord.getAbsences())
        {
          count++;
        }
        break;
      }
    }
  }
  return count;
}

Question 4 - getPointsForRow

Java - Final answer 4
public int getPointsForRow(int targetRow)
{
  int sum = 0;
  String firstColor = board[targetRow][0].getColor();
  boolean allSameColor = true;
  for (int col = 0; col < board[targetRow].length; col++)
  {
    Space current = board[targetRow][col];
    sum += current.getPoints();
    if (!current.getColor().equals(firstColor))
    {
      allSameColor = false;
    }
  }
  return allSameColor ? 2 * sum : sum;
}

Final Review Checklist

Before submitting any AP CSA free-response codeRead the method header one last time. Confirm the return type, parameter names and types, required side effects, and all preconditions/postconditions. Then trace one normal case and one boundary case.

Question-by-question checks

QuestionFinal checks
1AOriginal name checked first; suffix begins at 1; first available candidate assigned to username.
1BBoth the preceding character and hyphen are skipped; username is not modified; last index is safe.
2Two persistent fields; bottle starts full; subtract first; refill only when amount < 25% of capacity.
3IDs matched with .equals; both-enrollment condition enforced; comparison is >, not >=; lists unchanged.
4Only targetRow traversed; all points summed; all colors compared with .equals; double only after full scan.

Fast mental tests

  • Q1A: If requestedName is immediately available, does the constructor append nothing?
  • Q1B: For "A-B-C", does the method return "C"?
  • Q2: At exactly one quarter full, does the bottle stay at that amount?
  • Q3: Are equal absence counts excluded?
  • Q4: Does a mixed-color row return the undoubled sum even if most colors match?

Suggested 90-minute pacing

A practical plan is 20-22 minutes for Question 1, 18-20 minutes for Question 2, 18-20 minutes for Question 3, 18-20 minutes for Question 4, and the remaining time for compilation-style review. The strongest review targets are String comparison, loop bounds, strict versus non-strict inequalities, and whether required fields are actually updated.

Bottom lineThe four solutions use a candidate-search loop, a left-to-right String scan, persistent object state, nested list matching, and a one-row 2D-array traversal. Those patterns are broadly reusable across AP CSA free-response questions.

End of detailed solutions.