{"id":"recursion-strings-and-arrays","slug":"strings-and-arrays","title":"Recursion on strings and arrays","text":"A string is its first character followed by a shorter string, and that view makes many string problems recursive. `text.charAt(0)` is the first character, and `text.substring(1)` (module 16) is the rest, one character shorter. The base case is usually the empty string, which has no first character.\n\n### Counting a character\n\nA typesetter wants to know how many times a letter appears in a headline:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(count(\"banana bread\", 'a'));\n        System.out.println(count(\"\", 'a'));\n    }\n\n    public static int count(String text, char letter) {\n        if (text.isEmpty()) {\n            return 0;\n        }\n        int rest = count(text.substring(1), letter);\n        if (text.charAt(0) == letter) {\n            return rest + 1;\n        }\n        return rest;\n    }\n}\n```\n\n```output\n4\n0\n```\n\nEach call looks at one character and leaves the rest to the recursive call. The empty string has no `a` in it. Every call makes the string one character shorter, so the calls reach the empty string.\n\n### Arrays: pass an index\n\nFor arrays, the usual way is to pass an index instead of copying the rest (a copy on every call would take time and memory): the method gets the array and an index, the place where \"the rest\" starts, and the recursive call moves the index one place along. The base case is an index at the end of the array, where the rest is empty:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        int[] temperatures = {4, -2, 7, -5, 0};\n        System.out.println(countBelowZero(temperatures, 0));\n        System.out.println(countBelowZero(temperatures, 3));\n    }\n\n    public static int countBelowZero(int[] values, int start) {\n        if (start == values.length) {\n            return 0;\n        }\n        int rest = countBelowZero(values, start + 1);\n        if (values[start] < 0) {\n            return rest + 1;\n        }\n        return rest;\n    }\n}\n```\n\n```output\n2\n1\n```\n\n`countBelowZero(temperatures, 0)` counts the whole array; `countBelowZero(temperatures, 3)` counts only from index 3 on.\n\n### Building a string\n\nA recursive method can also return a string it builds from the smaller call's string. This one spaces out a word's letters for a sign:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(spaced(\"OPEN\"));\n    }\n\n    public static String spaced(String word) {\n        if (word.length() <= 1) {\n            return word;\n        }\n        return word.charAt(0) + \" \" + spaced(word.substring(1));\n    }\n}\n```\n\n```output\nO P E N\n```\n\nHere the base case is a word of at most one letter, which needs no spaces; the empty string is covered by it too.\n","task":"An ambulance has its name painted backwards on its hood, so that drivers ahead read it the right way around in their mirrors. Write the method `reverse(String text)`, which returns the text backwards, with recursion and no loop: the reverse of a text is the reverse of its rest (`text.substring(1)`) followed by its first character. Don't use `StringBuilder`. The check runs:\n\n```java\nSystem.out.println(reverse(\"AMBULANCE\"));\nSystem.out.println(reverse(\"stressed\"));\n```\n\nand expects:\n\n```\nECNALUBMA\ndesserts\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(reverse(\"AMBULANCE\"));\n        System.out.println(reverse(\"stressed\"));\n    }\n\n    public static String reverse(String text) {\n        return text;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(reverse(\"AMBULANCE\"));\n        System.out.println(reverse(\"stressed\"));\n    }\n\n    public static String reverse(String text) {\n        if (text.isEmpty()) {\n            return \"\";\n        }\n        return reverse(text.substring(1)) + text.charAt(0);\n    }\n}\n","hints":["The base case: the reverse of the empty string is the empty string.","For any other text, reverse the rest and put the first character after it: `return reverse(text.substring(1)) + text.charAt(0);`"],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(reverse(\"AMBULANCE\"));\nSystem.out.println(reverse(\"stressed\"));\n","expect":"ECNALUBMA\ndesserts","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(\"[\" + reverse(\"\") + \"]\");\nSystem.out.println(reverse(\"a\"));\nSystem.out.println(reverse(\"ab\"));\nSystem.out.println(reverse(\"live on time\"));\n","expect":"[]\na\nba\nemit no evil","hidden":true}],"require":[{"pattern":"\\breverse\\s*\\(","min":4,"message":"`reverse` should call itself."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Do it with recursion, without a loop."},{"pattern":"StringBuilder","message":"Don't use `StringBuilder`: build the string with recursion."}],"more":[{"task":"A word game gives bonus points for palindromes, words that read the same backwards, such as `kayak` or `noon`. Write the method `isPalindrome(String word)` with recursion and no loop: a word is a palindrome if its first and last characters are the same and the part between them is a palindrome. A word of 0 or 1 characters is always a palindrome. `word.substring(1, word.length() - 1)` is the part between the ends: `isPalindrome` itself calls itself on it, not through a helper method. Don't reverse the word: compare its ends. The check runs:\n\n```java\nSystem.out.println(isPalindrome(\"kayak\"));\nSystem.out.println(isPalindrome(\"noon\"));\nSystem.out.println(isPalindrome(\"kayaks\"));\n```\n\nand expects:\n\n```\ntrue\ntrue\nfalse\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(isPalindrome(\"kayak\"));\n        System.out.println(isPalindrome(\"noon\"));\n        System.out.println(isPalindrome(\"kayaks\"));\n    }\n\n    // Write the method isPalindrome here.\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(isPalindrome(\"kayak\"));\n        System.out.println(isPalindrome(\"noon\"));\n        System.out.println(isPalindrome(\"kayaks\"));\n    }\n\n    public static boolean isPalindrome(String word) {\n        if (word.length() <= 1) {\n            return true;\n        }\n        if (word.charAt(0) != word.charAt(word.length() - 1)) {\n            return false;\n        }\n        return isPalindrome(word.substring(1, word.length() - 1));\n    }\n}\n","hints":["Two base cases: a word of at most one character is a palindrome (`true`), and a word whose first and last characters differ isn't (`false`).","If the ends are equal, the answer is the answer for the middle: `return isPalindrome(word.substring(1, word.length() - 1));`"],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(isPalindrome(\"kayak\"));\nSystem.out.println(isPalindrome(\"noon\"));\nSystem.out.println(isPalindrome(\"kayaks\"));\n","expect":"true\ntrue\nfalse","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(isPalindrome(\"\"));\nSystem.out.println(isPalindrome(\"x\"));\nSystem.out.println(isPalindrome(\"xy\"));\nSystem.out.println(isPalindrome(\"xx\"));\nSystem.out.println(isPalindrome(\"racecar\"));\nSystem.out.println(isPalindrome(\"racecat\"));\nSystem.out.println(isPalindrome(\"abcdba\"));\n","expect":"true\ntrue\nfalse\ntrue\ntrue\nfalse\nfalse","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+boolean\\s+isPalindrome\\s*\\(\\s*String\\s+\\w+\\s*\\)","message":"Write the method as `public static boolean isPalindrome(String word)`."},{"pattern":"\\bisPalindrome\\s*\\(","min":5,"message":"`isPalindrome` should call itself on the part between the ends, not through a helper method."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Do it with recursion, without a loop."},{"pattern":"StringBuilder|\\breverse\\s*\\(","message":"Compare the word's ends, without reversing the word."}]},{"task":"A hiking trail is split into stages, and an array holds each stage's length in kilometers. Write the method `kilometersFrom(int[] stages, int start)`, which returns the total length of the stages from index `start` to the end of the array, with recursion and no loop: the stage at `start` plus the kilometers from `start + 1`. When `start` is the array's length, no stages are left, and the total is 0. The check runs:\n\n```java\nint[] stages = {12, 8, 15, 6, 9};\nSystem.out.println(kilometersFrom(stages, 0));\nSystem.out.println(kilometersFrom(stages, 3));\n```\n\nand expects:\n\n```\n50\n15\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        int[] stages = {12, 8, 15, 6, 9};\n        System.out.println(kilometersFrom(stages, 0));\n        System.out.println(kilometersFrom(stages, 3));\n    }\n\n    // Write the method kilometersFrom here.\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        int[] stages = {12, 8, 15, 6, 9};\n        System.out.println(kilometersFrom(stages, 0));\n        System.out.println(kilometersFrom(stages, 3));\n    }\n\n    public static int kilometersFrom(int[] stages, int start) {\n        if (start == stages.length) {\n            return 0;\n        }\n        return stages[start] + kilometersFrom(stages, start + 1);\n    }\n}\n","hints":["The base case: when `start` is `stages.length`, there are no stages left, so return 0.","Otherwise, add this stage to the rest: `return stages[start] + kilometersFrom(stages, start + 1);`"],"tests":[{"name":"Test 1","stdin":"","call":"int[] stages = {12, 8, 15, 6, 9};\nSystem.out.println(kilometersFrom(stages, 0));\nSystem.out.println(kilometersFrom(stages, 3));\n","expect":"50\n15","hidden":false},{"name":"Test 2","stdin":"","call":"int[] stages = {12, 8, 15, 6, 9};\nSystem.out.println(kilometersFrom(stages, 4));\nSystem.out.println(kilometersFrom(stages, 5));\nint[] none = new int[0];\nSystem.out.println(kilometersFrom(none, 0));\nint[] trail = {3, 21, 7};\nSystem.out.println(kilometersFrom(trail, 1));\n","expect":"9\n0\n0\n28","hidden":true},{"name":"Test 3","stdin":"","call":"int[] longTrail = new int[900];\nfor (int i = 0; i < longTrail.length; i++) {\n    longTrail[i] = i % 7;\n}\nSystem.out.println(kilometersFrom(longTrail, 0));\n","expect":"2694","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+int\\s+kilometersFrom\\s*\\(\\s*int\\s*\\[\\s*\\]\\s*\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public static int kilometersFrom(int[] stages, int start)`."},{"pattern":"\\bkilometersFrom\\s*\\(","min":4,"message":"`kilometersFrom` should call itself."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Do it with recursion, without a loop."}]}]}