Problem overview
CountPalindromes takes a string input and returns how many of its contiguous substrings are palindromes. Substrings are counted by position: two substrings with different start or end indexes are different, even when they have the same letters.
For "abbcbc" the answer is 9. The palindromes are the six single characters a, b, b, c, b, c, plus bb, bcb and cbc. Every single character is a palindrome, so the answer for a non-empty string is at least its length.
Approach
A substring is determined by its first and last index, i and j with i <= j. The brute force tries every such pair and asks a helper, IsPalindrome(input, i, j), whether the characters from i to j read the same in both directions. Each true adds 1 to the count.
The helper copies the substring, builds a reversed copy and compares the two. It is the simplest way to write the check, so the lesson uses it. It is not the cheapest, though. Each check takes time proportional to the substring's length, and there are about n²/2 substrings, so the whole algorithm is cubic. The next lesson on this problem counts palindromes by expanding outward from each center, which avoids the repeated work.
Here is the idea for "abbcbc". With i = 1 the loop tries j from 1 to 5: "b" yes, "bb" yes, "bbc" no, "bbcb" no, "bbcbc" no. That start contributes 2 palindromes. Doing the same for every start gives 9 in total.
The same helper could avoid the copies by comparing characters from both ends with two pointers, as a plain palindrome check does. That would save memory, but each check would still take linear time, so the total would stay cubic. The real improvement is to stop checking each substring separately, and that is what center expansion does: whenever a substring is not a palindrome, no larger substring around the same center can be one either, so the work stops early.
Step by step
- Declare
resultas anintset to 0. Every palindromic substring adds exactly one. - Loop over the start
ifrom 0 up to but not includinginput.Length. - Loop over the end
jfromito the end. Starting atirather than 0 visits each substring once, and includes the single characters, wherej == i. - Call the helper
IsPalindrome(input, i, j)and incrementresultwhen it returnstrue. Writing the caller before the helper keeps each piece small. - Return
resultafter both loops. - Declare the helper
IsPalindromeaboveCountPalindromes, taking the string and two indices, both inclusive, and returning abool. - Implement it. Take
firstasinput.Substring(left, right - left + 1), buildsecondas the reverse offirst, and return whether they are equal.
The solution in C#
class Solution {
public bool IsPalindrome(string input, int left, int right) {
string first = input.Substring(left, right - left + 1);
string second = new string(first.Reverse().ToArray());
return first == second;
}
public int CountPalindromes(string input) {
int result = 0;
for (int i = 0; i < input.Length; i++) {
for (int j = i; j < input.Length; j++) {
if (IsPalindrome(input, i, j)) {
result++;
}
}
}
return result;
}
}
The C# details are all in the helper:
Substring(start, length)takes a length, not an end index. That is why the code passesright - left + 1: the +1 makes the right end inclusive. Developers coming from Java, where the second argument is an end index, often get this wrong.first.Reverse()is a LINQ method. A string has no instance method namedReverse. The call comes fromSystem.Linq, which treats the string as a sequence ofcharand returns the characters in reverse order, without modifying the original.ToArray()andnew string(...). The reversed sequence is collected into achar[], and thestringconstructor turns that array back into a string.==compares contents. For C# strings,first == secondchecks whether the two strings hold the same characters, so it is the right comparison. It doesn't ask whether they are the same object.
Complexity
Time: O(n³). The loops produce about n²/2 pairs. For each, IsPalindrome copies, reverses and compares a substring of up to n characters, which is O(n).
Space: O(n). At any moment the helper holds a few strings and one array of at most n characters. The temporary strings are released after each call.
Edge cases and common mistakes
- The empty string. The outer loop never runs, so the result is 0.
- Repeated letters.
"aaa"has 6 palindromic substrings: three of length 1, two"aa"and one"aaa". Each position counts separately, so the two"aa"are different substrings. - Passing an end index to
Substring. Writinginput.Substring(left, right)reads the wrong number of characters and often throwsArgumentOutOfRangeExceptionwhenleft + rightexceeds the length. - Starting
jat 0. Thenright < leftfor some pairs, and the length passed toSubstringis zero or negative. Negative lengths throw. - Skipping single characters. They are palindromes, and the loops include them because
jstarts ati.
Testing
"abbcbc"gives9."aaa"gives6.""gives0.