Solution: Palindromic Substrings

seanpgallivan

seanpgallivan

Posted on March 27, 2021

Solution: Palindromic Substrings

This is part of a series of Leetcode solution explanations (index). If you liked this solution or found it useful, please like this post and/or upvote my solution post on Leetcode's forums.


Leetcode Problem #647 (Medium): Palindromic Substrings


Description:


(Jump to: Solution Idea || Code: JavaScript | Python | Java | C++)

Given a string, your task is to count how many palindromic substrings in this string.

The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.


Examples:

Example 1:
Input: "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".
Example 2:
Input: "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Constraints:

  • The input string length won't exceed 1000.

Idea:


(Jump to: Problem Description || Code: JavaScript | Python | Java | C++)

This problem, like many, is all about optimization. The naive solution would be to check if each and every substring is a palindrome, but that would easily achieve a TLE result.

Instead, the first realization that we can make is that each larger palindrome is built upon many layers of smaller palindromes, going back to its center. So we could optimize our solution by iterating through S and considering the index i to be the center of a series of potential palindromes.

Then, for each i we could use two more pointers (j & k) which would spread out in both directions from i. As long as S[j] == S[k], we'd know we had found a new palindrome and could continue spreading outward.

We would have to duplicate this process for even-length palindromes, as their center would be two characters intead of one.

But we can optimize more than that.

If we instead think of the center of the palindrome not as just one or two characters, but as any length of repeated characters, then we can break each iteration down into two steps.

First, we identify how long the "center" is by moving our right-size pointer (k) forwards while checking for duplicate characters. Now, instead of our center just being a single palindrome, it will be the Nth triangular number (defined as N * (N + 1) / 2) to account for all the smaller palindromes of which it's made.

After that, we can spread out with j and k just as before. Since we've dealt with the entire center's worth of palindromes, we can move i forward to start up again after the end of the center, regardless of its length.


Implementation:

The code for all four languages is very similar.


Javascript Code:


(Jump to: Problem Description || Solution Idea)

var countSubstrings = function(S) {
    let len = S.length, ans = 0
    for (let i = 0; i < len; i++) {
        let j = i - 1, k = i
        while (k < len - 1 && S[k] === S[k+1]) k++
        ans += (k - j) * (k - j + 1) / 2, i = k++
        while (~j && k < len && S[k] === S[j]) j--, k++, ans++
    }
    return ans
};
Enter fullscreen mode Exit fullscreen mode

Python Code:


(Jump to: Problem Description || Solution Idea)

class Solution:
    def countSubstrings(self, S: str) -> int:
        ans, n, i = 0, len(S), 0
        while (i < n):
            j, k = i - 1, i
            while k < n - 1 and S[k] == S[k+1]: k += 1                
            ans += (k - j) * (k - j + 1) // 2
            i, k = k + 1, k + 1
            while ~j and k < n and S[k] == S[j]:
                j, k, ans = j - 1, k + 1, ans + 1
        return ans
Enter fullscreen mode Exit fullscreen mode

Java Code:


(Jump to: Problem Description || Solution Idea)

class Solution {
    public int countSubstrings(String S) {
        int len = S.length(), ans = 0;
        for (int i = 0; i < len; i++) {
            int j = i - 1, k = i;
            while (k < len - 1 && S.charAt(k) == S.charAt(k+1)) k++;
            ans += (k - j) * (k - j + 1) / 2;
            i = k++;
            while (j >= 0 && k < len && S.charAt(k++) == S.charAt(j--)) ans++;
        }
        return ans;
    }
}
Enter fullscreen mode Exit fullscreen mode

C++ Code:


(Jump to: Problem Description || Solution Idea)

class Solution {
public:
    int countSubstrings(string S) {
        int len = S.length(), ans = 0;
        for (int i = 0; i < len; i++) {
            int j = i - 1, k = i;
            while (k < len - 1 && S[k] == S[k+1]) k++;
            ans += (k - j) * (k - j + 1) / 2, i = k++;
            while (~j && k < len && S[k++] == S[j--]) ans++;
        }
        return ans;
    }
};
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
seanpgallivan
seanpgallivan

Posted on March 27, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Solution: Redundant Connection
algorithms Solution: Redundant Connection

June 25, 2021

Solution: Out of Boundary Paths
algorithms Solution: Out of Boundary Paths

June 24, 2021

Solution: Pascal's Triangle
algorithms Solution: Pascal's Triangle

June 21, 2021

Solution: Swim in Rising Water
algorithms Solution: Swim in Rising Water

June 20, 2021