Skip to content
Marlbridge

AP® Computer Science A · Unit 2 · Topic 2.10

Implementing String Algorithms: Study Guide (Computer Science A 2.10)

Learn standard algorithms that process strings with loops, with a first-principles explanation, traced Java examples, a diagram, two worked examples and the mistakes to avoid.

Strings as sequences you can loop over

A String is an ordered sequence of characters, and you have already used methods to ask about it. Now you will combine those methods with loops to write real algorithms. The four methods you need are all in the Java Quick Reference:

Method What it returns
int length() the number of characters
String substring(int from, int to) the characters from index from up to, but not including, to
String substring(int from) the characters from from to the end
int indexOf(String str) the index of the first occurrence of str, or -1 if there is none
boolean equals(Object other) true if the characters are the same
int compareTo(String other) negative, zero or positive, by alphabetical order

A String has index values from 0 to one less than its length. Asking for a position outside that range causes a StringIndexOutOfBoundsException.

Indexes of the String rainbow and the substring from 2 to 5 The seven characters of the String rainbow are shown in boxes with indexes 0 to 6 beneath them: r 0, a 1, i 2, n 3, b 4, o 5, w 6. The boxes at index 2, 3 and 4 are drawn with a thick outline and a bracket beneath them labelled substring(2, 5) equals inb, because the end index 5 is not included. A note says length() is 7 and the last valid index is 6. r 0 a 1 i 2 n 3 b 4 o 5 w 6 String s = "rainbow"; s.length() is 7 substring(2, 5) is "inb" starts at index 2, stops before index 5 the last valid index is length() - 1 = 6
Figure 1. The String "rainbow" has length 7 and indexes 0 to 6. substring(2, 5) takes the characters at indexes 2, 3 and 4, giving "inb". The end index 5 is not included.
String s = "rainbow";
System.out.println(s.length());
System.out.println(s.substring(2, 5));
System.out.println(s.substring(3));
System.out.println(s.indexOf("bow"));
System.out.println(s.indexOf("z"));

Output:

7
inb
nbow
4
-1

Visiting every character

A single character at index i is the String s.substring(i, i + 1): from i up to, not including, i + 1. Looping i from 0 to s.length() - 1 visits them all.

String s = "rainbow";
for (int i = 0; i < s.length(); i++) {
    System.out.print(s.substring(i, i + 1) + "-");
}

Output:

r-a-i-n-b-o-w-

The boundary i < s.length() is exactly right. At i = 6 the call is substring(6, 7), which is allowed because the end index may equal the length. If the loop ran to i <= s.length(), then i = 7 would call substring(7, 8) and throw an exception.

The three standard string algorithms

1. Does the string have a property? Count characters that meet a criterion. This is the frequency pattern from Topic 2.9 with the characters as the “values”. To test membership in a set of letters, use indexOf on a String of those letters: a result of 0 or more means “found”.

2. Count substrings that meet a criterion. Slide a window of length k along the string. A window starting at index i is s.substring(i, i + k). The last window starts at s.length() - k, so the loop runs i <= s.length() - k.

3. Build a new string. Start with the empty string "" and add to it. Putting each new character on the front reverses the order.

public class StringTools {
    public static int countVowels(String s) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            String letter = s.substring(i, i + 1);
            if ("aeiou".indexOf(letter) >= 0) {
                count++;
            }
        }
        return count;
    }

    public static String reverse(String s) {
        String result = "";
        for (int i = 0; i < s.length(); i++) {
            result = s.substring(i, i + 1) + result;
        }
        return result;
    }

    public static boolean isPalindrome(String s) {
        return s.equals(reverse(s));
    }

    public static void main(String[] args) {
        System.out.println(countVowels("rainbow"));
        System.out.println(reverse("rainbow"));
        System.out.println(isPalindrome("racecar"));
        System.out.println(isPalindrome("rainbow"));
    }
}

Output:

3
wobniar
true
false

countVowels("rainbow") finds a, i and o: 3. In reverse, each pass puts the new letter before what has been built so far: “r”, “ar”, “iar”, “niar”, “bniar”, “obniar”, “wobniar”. A palindrome reads the same both ways, so the test is that the string equals its reverse. Notice that isPalindrome uses equals, not ==: the reverse is a new object, so == would compare references and be false.

Strings are immutable

A String object cannot be changed. Methods such as substring and the + operator return a new String and leave the original alone. So a call whose result is not stored has no effect on the variable:

String s = "cat";
s.substring(1);
String t = s.substring(1);
System.out.println(s + " " + t);

Output:

cat at

The first call computes “at” and throws it away. Only the second stores it in t. The loop that builds result works because each pass assigns the new, longer String back to result.

Out of range

Each of these throws StringIndexOutOfBoundsException:

String w = "banana";
String bad1 = w.substring(4, 7);
String bad2 = w.substring(7);
String bad3 = w.substring(-1, 2);

The String has length 6, so valid ends are at most 6, and valid starts are at least 0. substring(6) (start equal to the length) is allowed and returns the empty String, but substring(7) is not. When you write a window loop, always check the last window.

Comparing strings

equals tests whether two Strings have the same characters. compareTo orders them alphabetically: a negative result means the first String comes first, zero means they are equal, and a positive result means it comes later. Use the sign, not a particular value.

String a = "apple";
String b = "banana";
System.out.println(a.equals(b));
System.out.println(a.compareTo(b) < 0);
System.out.println(b.compareTo(a) > 0);
System.out.println(a.compareTo("apple") == 0);

Output:

false
true
true
true

Worked example 1: counting a letter and building a new string

Question. Write a method countLetter(String s, String letter) that counts the occurrences of a one-letter String, and a method removeLetter(String s, String letter) that returns s with that letter left out. Use them on “banana” with “a”.

public class LetterTools {
    public static int countLetter(String s, String letter) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.substring(i, i + 1).equals(letter)) {
                count++;
            }
        }
        return count;
    }

    public static String removeLetter(String s, String letter) {
        String result = "";
        for (int i = 0; i < s.length(); i++) {
            String current = s.substring(i, i + 1);
            if (!current.equals(letter)) {
                result = result + current;
            }
        }
        return result;
    }

    public static void main(String[] args) {
        System.out.println(countLetter("banana", "a"));
        System.out.println(removeLetter("banana", "a"));
    }
}

Output:

3
bnn

Trace for removeLetter("banana", "a").

i current is it “a”? result after
0 b no b
1 a yes b
2 n no bn
3 a yes bn
4 n no bnn
5 a yes bnn

Check. “banana” has three a’s, so countLetter is 3, and removing them leaves b, n, n. The original s is unchanged, because the method builds a new String. This follows the “build a new string” pattern: the new letter goes on the end (result + current) to keep the original order.

Worked example 2: counting substrings with a window

Question. Write countSub(String s, String target) that counts how many times target occurs in s, allowing occurrences to overlap. Find countSub("banana", "ana").

Plan. The window has the length of target, which is k = target.length(). It starts at i, and s.substring(i, i + k) is the window. The last start is s.length() - k, so the loop test is i <= s.length() - k.

public class CountSub {
    public static int countSub(String s, String target) {
        int count = 0;
        int k = target.length();
        for (int i = 0; i <= s.length() - k; i++) {
            if (s.substring(i, i + k).equals(target)) {
                count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(countSub("banana", "ana"));
        System.out.println(countSub("banana", "na"));
        System.out.println(countSub("banana", "x"));
    }
}

Output:

2
2
0

Trace for countSub("banana", "ana"). Here k = 3 and the loop runs for i from 0 to 6 - 3 = 3.

i window substring(i, i + 3) equals “ana”? count
0 ban no 0
1 ana yes 1
2 nan no 1
3 ana yes 2

Check. The two occurrences, at indexes 1 and 3, overlap on the middle “a”. Note what happens with a wrong boundary. With i < s.length() - k the last window, at i = 3, would be missed and the answer would be 1. With i < s.length() the window at i = 4 would be substring(4, 7), which is out of range and throws an exception.

How this appears in exam questions

String algorithms in an exam use only the methods in the Java Quick Reference: length, substring (both forms), indexOf, equals and compareTo. To look at one character you write substring(i, i + 1), and to compare it you use equals. Two habits protect you from the most common lost marks. First, check the last index you use: for single characters the loop stops at length() - 1, and for a window of k characters it stops at length() - k. An index outside the String throws a StringIndexOutOfBoundsException, which an examiner will treat as a bug. Second, remember that a String cannot be changed: if the task says to produce a modified String, build a new one in a variable and return that variable. When a question gives you a precondition about the input, such as words separated by single spaces, you can rely on it.

Common misconceptions

  • “substring(a, b) includes the character at index b.” It stops before b, so the length of the result is b - a.
  • “The last index is length().” It is length() - 1. substring(length()) is the empty String, but substring(length(), length() + 1) is out of range.
  • “== compares strings.” It compares references. Use equals.
  • “s.substring(1); shortens s.” Strings are immutable: the result must be assigned.
  • “The window loop can run to length().” Each window must fit inside the String, so the last start is length() - k.
  • “indexOf returns true or false.” It returns an index, with -1 for not found. Test with >= 0.
  • “Building a new string in a loop changes the original.” The original is untouched; only the variable holding the result changes.

Where this leads

String algorithms use the same patterns as Topic 2.9: counting, searching and building. In Unit 4 the same ideas apply to arrays and ArrayList objects, and the searching and sorting algorithms of Topics 4.14 and 4.15 use compareTo on strings. Try the practice questions, recap with the revision notes and tick off the checklist. Then read the Topic 2.11 study guide on nested iteration. For the loop patterns, revisit the Topic 2.9 study guide.

Quick questions

How do I get one character of a String?

Use substring(i, i + 1), which returns a String of length 1. Compare it with another String using equals. This course's Java Quick Reference gives substring, length, indexOf, equals and compareTo as the String methods.

What is a StringIndexOutOfBoundsException?

The error thrown when an index or range is outside the String. For a String of length n, indexes must be between 0 and n - 1, and substring(a, b) needs 0 <= a <= b <= n.

Why does s.substring(1); not change s?

Strings are immutable. The method returns a new String and leaves s unchanged. If you want to keep the result you must assign it, for example s = s.substring(1);.

How do I reverse a String?

Start with an empty String and loop through the original from the first character to the last. On each pass put the current character in front of the result: result = s.substring(i, i + 1) + result;.

AP® and Advanced Placement® are trademarks registered by the College Board, which is not affiliated with, and does not endorse, this website. Marlbridge is an independent tuition provider. It is not an authorized AP course provider, an AP school or an exam centre, and these resources are not official College Board materials. Reading them or doing simulations does not meet the laboratory requirement of an AP science course.