Problem overview
The method countPalindromes(input) receives a string and returns how many of its contiguous substrings are palindromes. A palindrome reads the same forwards and backwards, such as "bcb". Substrings are counted by position, not by content: if the same text appears at two different places, it counts twice. In "abbcbc" the answer is 9:
a b b c b c (6 single characters)
bb (indices 1 to 2)
bcb (indices 2 to 4)
cbc (indices 3 to 5)
Every single character is a palindrome, so a string of length n always scores at least n. For "abc" the answer is exactly 3, for "aaa" it is 6 (three single letters, two "aa" and one "aaa"), and for the empty string it is 0.
Approach: check every substring
The lesson takes the direct route. A substring is fixed by its start index i and its end index j, with i <= j. Two nested loops visit every such pair exactly once. For each pair, a helper decides whether the text between i and j is a palindrome, and when it is, the count goes up by one.
A string of length n has n * (n + 1) / 2 substrings: 21 for the 6 characters of "abbcbc", and 500,500 for a string of 1,000 characters. Checking each one costs time proportional to its length, so the total grows with the cube of n. That is fine for short strings and slow for long ones. A faster way, which grows palindromes outwards from each possible centre in O(n²), is the subject of the next lesson on this problem. This one practises the plain version first.
Step by step
- Start a counter.
resultis declared withletand set to 0. Every palindromic substring adds exactly one. - Loop over start indices.
iruns from 0 up to, but not including,input.length. - Loop over end indices.
jstarts ati, not at 0, so that each substring is visited once. Starting atialso meansj == iis the single-character case, which is always a palindrome. - Test and count. If
this.isPalindrome(input, i, j)istrue, incrementresult. Because the helper is a method of the class, the call goes throughthis. - Return the counter after both loops finish.
- Write the helper
isPalindrome(input, left, right). Both indices are inclusive. - Cut out the substring.
input.substring(left, right + 1)takes the characters fromleftup to but not including its second argument, so the end needs the+ 1. - Reverse it. JavaScript strings have no
reversemethod, but arrays do. Sosplit("")turns the string into an array of characters,reverse()reverses that array, andjoin("")glues it back into a string. - Compare the substring with its reversed copy and return the result of
first == second.
The solution in JavaScript
class Solution {
isPalindrome(input, left, right) {
let first = input.substring(left, right + 1);
let second = first.split("").reverse().join("");
return first == second;
}
countPalindromes(input) {
let result = 0;
for (let i = 0; i < input.length; i++) {
for (let j = i; j < input.length; j++) {
if (this.isPalindrome(input, i, j)) {
result++;
}
}
}
return result;
}
}
Details worth knowing:
substringtakes an exclusive end."abc".substring(0, 2)is"ab". That is why the helper usesright + 1while the loops treatjas an inclusive index.reverse()changes the array it is called on. That is harmless here, becausesplit("")creates a fresh array on every call. The stringfirstis never changed, since strings in JavaScript are immutable.split("")splits into UTF-16 code units. For ordinary letters this is the same as characters. For emoji and some other symbols, which take two code units, reversing would scramble the pair. The lesson's inputs are plain text, so this does not matter here.- Comparison is case-sensitive.
"Aa"has two palindromes, its two single letters, because"Aa"reversed is"aA", which is a different string.
Complexity
There are about n² / 2 substrings, and for each one the helper copies, splits, reverses and joins up to n characters. The time is O(n³). The extra space is O(n), for the temporary substring and array built inside one call of the helper; they are discarded after each call.
The cost can be reduced without changing the idea. Comparing characters from both ends inwards with two indices needs no copies and stops at the first mismatch, but the worst case, a string of one repeated letter, is still O(n³). Growing palindromes outwards from each centre brings it down to O(n²) in the worst case.
Edge cases and common mistakes
- Forgetting the single characters. The inner loop must start at
i, noti + 1, or thenone-letter palindromes are lost."abc"would give 0 instead of 3. - Off-by-one in
substring. Usinginput.substring(left, right)drops the last character, so every single-character substring becomes the empty string, which counts as a palindrome. The code still runs but checks the wrong text, and the count is wrong. - Counting distinct palindromes. The problem counts positions. In
"aaa","a"appears three times and counts three times, which gives 6 in total. ASetof texts would give 3. - Calling the helper without
this. Inside a class method,isPalindrome(...)by itself is looked up as a variable and fails with aReferenceError. Writethis.isPalindrome(...). - Empty input. The outer loop does not run, so the result is 0.
Testing
Try these in the editor:
"abbcbc"returns9."aaa"returns6."abc"returns3."abba"returns6."racecar"returns10.""returns0.
