Start learning free

LESSON 225 OF 348 · TWO POINTERS

Palindrome Check in Kotlin: Two Pointers in O(n) Time, O(1) Space

Practice O(n) Read the full explanation

Given a string, determine if it is a palindrome.

A palindrome is a word, number, phrase, or other sequence of characters which reads the same backward as forward

Example 1:

Input:  "madam racecar madam"

Output: true

Example 2:

Input:  "abcbx"

Output: false

Note:

Your algorithm should run in O(n) time and use O(1) extra space.


Hints:

For the string to be a palindrome, for each character, there is a corresponding element which should be equal to that one:
  • s[0] == s[n - 1]
  • s[1] == s[n - 2]
  • s[2] == s[n - 3]
  • ...
How can we check all these pairs of corresponding characters while traversing the string one time?

We can use two pointers! One index variable i starting from the beginning (i = 0) and one index variable j starting from the end of the string (j = n - 1). How should we move these pointers throught the algorithm?

For as long as i < j:
  • 1. We should check if s[i] != s[j] and if so, we conclude the string is not a palindrome and return false.
  • 2. Move i one step to the right.
  • 3. Move j one step to the left.
  • 4. Go to step 1.
If i became greater than or equal to j and we haven't returned false, then we can conclude the string is a palindrome and return true.

STEP 1 OF 6
    Ctrl + Enter to check

    KOTLIN · TWO POINTERS

    Palindrome Check in Kotlin, explained

    Problem overview

    The function isPalindrome takes a String named input and returns a Boolean: true if the string reads the same forwards and backwards, false otherwise. The statement asks for O(n) time and O(1) extra space.

    In the first example, "madam racecar madam" is a palindrome. The first and last characters are both m, the second and second to last are both a, and so on, all the way to the space in the middle that matches itself. "abcbx" is not: its first character a differs from its last, x.

    The check is literal. Spaces count as characters, and A and a are different characters, so "Aba" is not a palindrome.

    Approach: compare from both ends

    If a string has length n, it is a palindrome exactly when input[0] == input[n - 1], input[1] == input[n - 2], and so on for every pair of mirrored positions. The simplest idea is to build a reversed copy and compare it with the original. That works, but the copy takes O(n) extra space, which the statement rules out.

    Two pointers avoid the copy. One pointer starts at the first index and one at the last. They compare their characters and move one step towards each other. As soon as a pair differs, the answer is false. If the pointers meet or cross without a mismatch, every pair matched and the answer is true.

    Each pair is checked once, from the outside in, so the work is linear. Only two integers are stored, so the extra space is constant.

    Here is the idea on "abcbx". The pointers start at indices 0 and 4, which hold a and x. They differ, so the function returns false right away, without looking at the middle. On "abcba" the pairs a/a and b/b match, the pointers meet at index 2, the loop ends and the function returns true.

    Step by step

    1. Create left as a var set to 0, the first index. It must be a var because it changes in the loop.
    2. Create right as a var set to input.length - 1, the last index.
    3. Loop while left < right. While the pointers haven't met, there is still a pair to compare. With an odd length, the middle character is never compared, which is fine because it matches itself.
    4. Return false on a mismatch. If input[left] != input[right], one bad pair is enough to decide the answer.
    5. Move both pointers inward with left++ and right--. Without this, the loop would compare the same pair forever.
    6. Return true after the loop. The code only gets here if every pair matched.

    The solution in Kotlin

    class Solution {
        fun isPalindrome(input: String): Boolean {
            var left = 0
            var right = input.length - 1
            while (left < right) {
                if (input[left] != input[right]) {
                    return false
                }
                left++
                right--
            }
            return true
        }
    }
    

    Kotlin details worth knowing:

    • Indexing a string gives a Char. input[left] is a single character, and comparing two Char values with != compares the characters themselves.
    • != compares contents. In Kotlin, == and != call equals, so they test equality of values, not whether two references are the same object. That is what you want for characters and also for whole strings.
    • Strings are immutable. The function only reads input, so nothing is copied or changed.
    • var versus val. left and right are reassigned, so they must be var. A val could be set once only.
    • The early return. Returning from inside the loop ends the function immediately, so a string that differs at its ends is rejected after one comparison.
    • Characters are UTF-16 units. A character outside the basic range, such as many emoji, takes two units in a Kotlin string. For such text this loop compares units, not whole symbols. For the plain letters in the lesson it makes no difference.

    Complexity

    Time: O(n). The pointers start n - 1 apart and close by 2 per iteration, so the loop runs about n / 2 times at most. Each iteration does constant work.

    Space: O(1). Two integers are stored, regardless of the string length.

    Edge cases and common mistakes

    • Empty string. right starts at -1, so left < right is false and the function returns true. An empty string reads the same in both directions.
    • One character. "a" gives true: both pointers are at index 0, so the loop doesn't run.
    • Even and odd lengths. "abba" ends when the pointers cross, and "abcba" ends when they meet on c. Both give true.
    • Using left <= right. It compares the middle character with itself, which is harmless but unnecessary.
    • Forgetting to move the pointers. The loop never ends if both pointers stay put on matching characters.
    • Returning true inside the loop. One matching pair says nothing about the other pairs. Only the mismatch can be decided early.
    • Case and spaces. "Aba" and "a b" are not palindromes here, because the code compares exact characters.

    Testing

    • "madam racecar madam" gives true.
    • "abcbx" gives false.
    • "" gives true.
    • "a" gives true.
    • "ab" gives false.
    • "Aba" gives false.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free