String practice problems
Reversing, counting, checking palindromes, and other classic string exercises solved step by step.
Finished reading?
Mark this session so you can track where you are.
Reversing, counting, checking palindromes, and other classic string exercises solved step by step.
Finished reading?
Mark this session so you can track where you are.
You have the tools now: a string knows its length(), it can hand you any character with charAt, and you can walk it with a loop. This session is one long workout. We take five classic string problems, the kind that show up in every interview and every first job, and solve each one slowly, out loud, with the index walking the string in front of you.
for loop and charAt.length(), charAt, substring, equals, and the fact that a string is immutable, so we never change one in place, we build a fresh one. We also lean on loops and the idea of a char as a small number from variables and types. No new language features today, only practice.i that walks from 0 up to s.length() - 1, pulling out one character at a time with s.charAt(i). Once that rhythm is in your fingers, half of string programming is just deciding what to do with each character as it goes by.The problem. Given a string like "java", produce a new string with its characters in the opposite order: "avaj".
The approach. A string is immutable, so we cannot shuffle "java" in place. Instead we start with an empty string and glue characters onto it. The trick is the direction we read from. If we walk the original from its last index down to its first and append each character as we go, the new string comes out reversed.
public class Main { public static void main(String[] args) { String s = "java"; String reversed = ""; for (int i = s.length() - 1; i >= 0; i--) { reversed = reversed + s.charAt(i); } System.out.println(reversed); }}avaj
Press Run and watch i count down: 3, 2, 1, 0. At each step it grabs one character from the end and tacks it onto reversed. The build order is "a", then "av", then "ava", then "avaj".
reversed = reversed + s.charAt(i). Read it right to left. s.charAt(i) is the character at the current index. The + joins it to whatever reversed already holds, and the assignment stores that longer string back into reversed. Because we are reading s from the end, the last character lands first and the first character lands last.
for loop does not have to go up. i-- walks it down, and i >= 0 is the condition that keeps it going while the index is still a valid position. Same loop, reversed direction.The problem. Given a word, count how many of its letters are vowels (a, e, i, o, u). For "Programming" the answer is 3.
The approach. Walk the string forwards this time. Keep a counter that starts at 0. For each character, ask “is this one of the five vowels?” and if so, add one to the counter. We lower-case the whole string first so that an upper-case A counts just the same as a lower-case a.
public class Main { public static void main(String[] args) { String s = "Programming"; s = s.toLowerCase(); int count = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') { count = count + 1; } } System.out.println("Vowels: " + count); }}Vowels: 3
After toLowerCase, the string is "programming". As i walks across it, the counter ticks up at three places: the o, the a, and the i. Everything else is a consonant and is skipped. The program prints Vowels: 3.
The big if condition. c == 'a' || c == 'e' || ...reads as “is c an a, or an e, or any of the others?”. The || (or) chains the five possibilities together: if any one of them is true, the whole condition is true and we count the character. Note the single quotes: 'a' is a char, the same type charAt hands back, so the comparison is char against char.
The problem. A palindrome reads the same forwards and backwards: "racecar", "level", "noon". Given a string, decide true or false.
The approach. We could reverse the string and compare, but there is a tighter idea. Use two pointers. One starts at the front, one at the back. Compare the characters they point at. If they ever differ, it is not a palindrome and we can stop. If the front pointer reaches the middle without finding a mismatch, every pair matched and it is a palindrome.
We do not even need two separate variables. The character at the front is s.charAt(i), and its mirror at the back is s.charAt(s.length() - 1 - i). As i grows, that mirror index shrinks. We only need to walk i to the middle, so the loop runs while i < s.length() / 2.
public class Main { public static void main(String[] args) { String s = "racecar"; boolean isPalindrome = true; for (int i = 0; i < s.length() / 2; i++) { char front = s.charAt(i); char back = s.charAt(s.length() - 1 - i); if (front != back) { isPalindrome = false; } } System.out.println(isPalindrome); }}true
For "racecar" the length is 7, so the loop runs while i < 3, that is for i = 0, 1, 2. Watch the pairs line up: r with r at index 6, a with a at index 5, c with c at index 4. The middle e at index 3 has no partner and does not need one. Nothing ever mismatched, so it prints true.
s.charAt(s.length() - 1 - i). The last valid index of any string is s.length() - 1 (because indexing starts at 0). Subtracting i from that walks the mirror inward as i walks the front outward. When i is 0 the mirror is the very last character. When i is 1 it is the second-to-last. They march toward each other and meet in the middle.
s.charAt(s.length() - i) and forget the extra - 1. On the first round that asks for s.charAt(s.length()), an index one past the end, which crashes with a StringIndexOutOfBoundsException. Whenever you reach from the back, the back index is length - 1, never length.To prove the false case, change s to "hello" and run again. On the very first round, front is h and back is o. They differ, the flag flips to false, and the program prints false.
The problem. Given a string and a target character, count how many times that character appears. In "yellow umbrella", the letter l appears 4 times.
The approach.This is the vowel counter with a simpler question. Walk the string, and for each character ask the single question “does this equal my target?”. If yes, add one.
public class Main { public static void main(String[] args) { String s = "yellow umbrella"; char target = 'l'; int count = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == target) { count = count + 1; } } System.out.println("'" + target + "' appears " + count + " times"); }}'l' appears 4 times
The l shows up twice in yellow (at index 2 and 3), then twice more in umbrella (at index 12 and 13). Four hits in all, so the program prints 'l' appears 4 times.
if (s.charAt(i) == target). Both sides are a char, so == compares them directly. We pulled the target out into its own variable so the same code counts any character: change target to 'e'and the very same loop now counts the e's. Naming the thing you search for is a small habit that makes code easy to reuse.
a e i o u?|| chain of five checks.== check.Counting problems are all the same skeleton: a counter at zero, a walk across the string, and one question asked of each character. Only the question changes.
The problem. Find the first character in a string that appears exactly once. In "swiss", the s repeats and the w is the first letter that stands alone, so the answer is w.
The approach.For each character, we need to know “does this character appear anywhere else in the string?”. We can answer that by counting how many times it shows up across the whole string. If that count is exactly 1, this character is unique, and because we check from the front, the first such character we meet is our answer.
Counting one character means a second loop inside the first: the outer loop picks a character, the inner loop walks the whole string counting how often that character appears. A loop inside a loop is called a nested loop.
public class Main { public static void main(String[] args) { String s = "swiss"; char answer = '?'; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); int count = 0; for (int j = 0; j < s.length(); j++) { if (s.charAt(j) == c) { count = count + 1; } } if (count == 1) { answer = c; break; } } System.out.println("First non-repeating: " + answer); }}First non-repeating: w
Step through it slowly. The outer loop first picks sat index 0. The inner loop counts the s's across "swiss" and finds 2 (index 0 and index 3), so s is not unique and we move on. Next the outer loop picks w at index 1. The inner loop counts and finds only 1. That is our answer: we store w and break out. The program prints First non-repeating: w.
The pair answer = c; then break;. The moment the inner count comes back as one, we have found the first unique character. There is no reason to keep searching, so break leaves the outer loop immediately. Without it, the loop would keep going and could overwrite answer with a later unique character. break is what makes it the first one and not the last.
Five problems, but only a few ideas underneath them. Naming the shape is what lets you solve the sixth problem you have never seen before.
"" and concatenate, as in reverse.Try each one yourself first, then open the answer.
"noon" is a palindrome, and which pairs of characters does it compare?"java" into "Java", using only methods you know.for (int i = 0; i < s.length(); i++), still appending s.charAt(i) to reversed?j = 0 and not at j = i + 1?Take these away. They continue exactly what we just did.
"banana" the result is "ban". Build a fresh result string, and before adding a character, check whether the result already contains it."abc" becomes "def". Walk the string, turn each character into its number, add the shift, turn it back into a character, and append it to a result string."hello" and then decoding the result returns "hello"."listen" and "silent" are anagrams. Using only loops and the counting pattern, for each character in the first word, count its appearances in both words and confirm the counts match.