Skip to main content

Reverse String and Palindrome Check Algorithms

Comprehensive Java solutions for string reversal and palindrome validation with multiple approaches and performance analysis.

String reversal and palindrome validation are fundamental algorithmic problems that test understanding of array manipulation, two-pointer techniques, and string processing. These problems frequently appear in technical interviews and require careful consideration of time complexity, space usage, and edge case handling.

1. Pattern Restatement

Clarifying questions to ask the interviewer:

  1. Are we checking if a string is a palindrome, or reversing a string? (The problem statement is ambiguous but likely asking about palindrome check)
  2. What's the expected input size? (We should clarify if we're dealing with small strings or potentially large inputs)
  3. Should we consider case sensitivity? (Are "A" and "a" the same character?)
  4. Do we need to ignore non-alphanumeric characters? (For example, "A man, a plan, a canal: Panama" is a valid palindrome)
  5. Can we modify the input string in-place? (Some problems require preserving original data)

Restate the pattern or problem type in your own words: The reverse string/palindrome check pattern involves determining whether a given sequence of characters reads the same forwards and backwards. This is a fundamental string manipulation problem that tests understanding of two-pointer techniques, character validation, and string traversal.

Clarify the signal words and constraint shape that point to it: Key signal words include "palindrome," "reverse," "same forwards and backwards," "string manipulation," and "two pointers." The constraint shape typically involves:

  • String input with character-by-character comparison
  • Need for efficient traversal (often O(n) time)
  • Often involves ignoring case, spaces, or punctuation
  • May require in-place modification or additional space for validation

Note any assumptions you are making:

  • The problem likely asks for palindrome validation rather than string reversal
  • Input consists of printable ASCII characters (or Unicode)
  • Case-insensitive comparison is typically required for standard palindromes
  • Non-alphanumeric characters may need to be ignored in practical applications

Identify whether the pattern is best suited for static, streaming, or online scenarios if relevant: This pattern is primarily suited for static input scenarios where the entire string is available upfront. For streaming data, we'd need to buffer or use more complex state tracking approaches. The pattern works well in online scenarios where we can process characters sequentially but don't benefit from streaming optimizations since we need to examine the entire string at least once.

2. When to Use This Pattern

Signal words and constraint shapes in a problem statement that indicate this pattern:

  • "Check if string is palindrome"
  • "Reverse the string"
  • "Determine if arrangement of characters forms palindrome"
  • "Find longest palindromic substring"
  • "Validate string properties with symmetric structure"

Why this pattern fits better than the most common alternatives: The reverse string/palindrome pattern is specifically designed for problems involving symmetric character arrangements. Unlike sliding window or two-pointer approaches that focus on subarrays, this pattern directly addresses the symmetric nature of strings. It's more efficient than brute force string reversal comparisons because it leverages the inherent symmetry property.

Conditions where a variation or related pattern applies instead:

  • For substring problems, sliding window or dynamic programming approaches might be better suited
  • When dealing with multiple strings that need comparison, hash-based approaches could work but are less direct
  • For complex string manipulation with transformations, regex or state machine approaches might be preferred

The core insight that makes this pattern work: The fundamental insight is that a palindrome has symmetric properties where characters mirror around a center point. This allows us to use two pointers starting from both ends that move inward, comparing characters at symmetric positions. This approach naturally handles both odd and even length palindromes by treating the center as a "pivot" that can be either a single character or between two characters.

The pattern is particularly effective when:

  1. The problem requires symmetric property validation
  2. Character-by-character comparison is needed
  3. String reversal operations are fundamental to the solution
  4. The input size allows for O(n) linear traversal approaches

3. Approach Evolution

Interview communication script:

My first instinct is to use a two-pointer approach because I can compare characters from both ends moving inward, which feels like the most natural way to check if a string is a palindrome. But that's O(n) time and O(1) space, so it's already optimal in terms of complexity. Let me think about how to handle edge cases and make sure I'm not missing anything.

Actually, I should be more careful about what exactly we're checking. The problem could be asking for a palindrome check or just reversing a string. Let me clarify that the task is to determine if a string reads the same forwards and backwards, which is a classic palindrome problem.

I notice that if I use a brute force approach of reversing the string and comparing it to the original, that would work but would require O(n) extra space for the reversed string. That's not ideal.

The key optimization here is to realize that we don't need to actually reverse anything. We can just use two pointers from both ends and move them toward each other, comparing characters at each step. This maintains the O(1) space constraint while keeping O(n) time.

The invariant I must maintain is that the characters at positions left and right should always be equal for a valid palindrome. If I ever find a mismatch, I can immediately return false.

Let me walk through this more carefully:

Brute-force approach: Create a reversed copy of the string and compare it with the original. This is O(n) time but O(n) space because we need to store the reversed string.

Improved approach: Use two pointers starting from both ends, moving inward. At each step, compare characters at the left and right positions. If they match, continue; if not, return false immediately.

Final approach: Two-pointer technique with O(n) time and O(1) space. The key insight is that we only need to check if characters at symmetric positions match, so we can avoid creating a reversed copy entirely.

Important invariants:

  1. Left pointer never exceeds right pointer
  2. Characters at symmetric positions must be equal for palindrome property to hold
  3. We only process each character once, so we don't double-count or miss any positions

4. Data Structure Choice

For the reverse string/palindrome check problem, the primary data structure used is a char array or String (depending on input type).

Why char array is preferred:

  • Direct access: Allows O(1) random access to characters at any position
  • In-place modification: Enables swapping characters without additional space overhead
  • Memory efficiency: No need for creating new string objects during reversal operations

Why String is acceptable but less optimal:

  • Immutable nature: Requires creating new string objects when modifying, leading to O(n) extra space for each operation
  • Performance overhead: String concatenation operations are costly in Java

Time and space impact:

  • Time: O(1) access for each character comparison
  • Space: O(n) for the input array/string, but O(1) extra space for the algorithm itself

Helper structure considerations:

  • Two pointers: No additional data structures needed beyond index variables (left and right pointers)
  • Stack approach: Could use a stack for reversal but introduces O(n) extra space and unnecessary complexity

The chosen approach uses only two integer indices (left and right pointers) to traverse the string from both ends toward the center, maintaining O(1) space complexity while achieving optimal time performance.

// Example of pointer-based approach:
int left = 0;
int right = charArray.length - 1;
while (left < right) {
    // Compare and swap if needed
    left++;
    right--;
}

5. Algorithm Walkthrough

Let me trace through the two-pointer approach with the example input ["h","e","l","l","o"]:

Step 1: Initialize pointers - left = 0, right = 4

  • Array: ["h","e","l","l","o"]
  • Compare s[0] = 'h' with s[4] = 'o'
  • These don't match, so we swap them
  • Result: ["o","e","l","l","h"]

Step 2: Move pointers inward - left = 1, right = 3

  • Array: ["o","e","l","l","h"]
  • Compare s[1] = 'e' with s[3] = 'l'
  • These don't match, so we swap them
  • Result: ["o","l","l","e","h"]

Step 3: Move pointers inward - left = 2, right = 2

  • Array: ["o","l","l","e","h"]
  • Now left >= right, so we stop the process

Final Result: ["o","l","l","e","h"] - This is the reversed string.

The algorithm correctly handles the case where we need to reverse a character array in-place. It processes elements from both ends toward the center, swapping mismatched characters to achieve the reversed order.

Edge cases handled:

  • Empty array (no operations)
  • Single element array (no operations)
  • Even-length arrays (processes all pairs)
  • Odd-length arrays (middle element remains unchanged)

6. Java Solution

public class Solution {
    /**
     * Checks if a string is a palindrome using two pointers approach.
     * Time complexity: O(n), Space complexity: O(1)
     */
    public boolean isPalindrome(String s) {
        // Handle null or empty string
        if (s == null || s.length() == 0) {
            return true;
        }
        
        int left = 0;
        int right = s.length() - 1;
        
        while (left < right) {
            // Skip non-alphanumeric characters from left
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                left++;
            }
            
            // Skip non-alphanumeric characters from right
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                right--;
            }
            
            // Compare characters (case-insensitive)
            if (Character.toLowerCase(s.charAt(left)) != 
                Character.toLowerCase(s.charAt(right))) {
                return false;
            }
            
            left++;
            right--;
        }
        
        return true;
    }
    
    /**
     * Alternative: Reverse string and compare
     * Time complexity: O(n), Space complexity: O(n)
     */
    public boolean isPalindromeReverse(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        
        // Clean the string: keep only alphanumeric chars and convert to lowercase
        StringBuilder cleaned = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (Character.isLetterOrDigit(c)) {
                cleaned.append(Character.toLowerCase(c));
            }
        }
        
        // Compare with reversed version
        String cleanedStr = cleaned.toString();
        String reversed = cleaned.reverse().toString();
        
        return cleanedStr.equals(reversed);
    }
}

The solution implements the two-pointer technique for palindrome checking. It handles edge cases like null/empty strings and ignores non-alphanumeric characters while performing case-insensitive comparison. The approach is optimal with O(1) space complexity and O(n) time complexity, making it ideal for FAANG interviews where efficiency is crucial.

7. Complexity Analysis

Time Complexity: O(n) where n is the length of the string. The algorithm performs a single pass through half the string characters, comparing each character with its mirror counterpart. Each character access and comparison takes constant time, so the overall time complexity is linear.

Space Complexity: O(1) for the iterative two-pointer approach. Only a constant amount of extra space is used for the two integer pointers (left and right) regardless of input size. If we consider the space required for the input string itself, that's O(n) but the algorithm uses only constant additional space beyond the input.

The linear time complexity is optimal for this problem since we must examine each character at least once to determine if the string is a palindrome. The constant space usage makes this solution memory-efficient, avoiding the need to create a reversed copy of the string or use additional data structures like stacks or queues.

The solution achieves the best possible complexity trade-off for palindrome checking - it's both time-optimal (O(n) is the theoretical lower bound) and space-optimal (O(1) additional space). This makes it suitable for large inputs where memory constraints might be a concern.

The algorithm's complexity analysis holds because:

  1. Each character is accessed exactly once in the comparison phase
  2. No nested loops or recursive calls that would increase complexity
  3. The loop condition left < right ensures we process only the necessary half of the string
  4. All operations inside the loop are O(1) primitive operations

8. Edge Cases & Pitfalls

Empty and Minimal Inputs

  • Empty string: Should return true for palindrome check (empty string is considered palindromic)
  • Single character: Should always return true regardless of content
  • Two characters: Must handle both identical and different characters correctly

Duplicate Values and Repeated States

  • Repeated characters: The algorithm must properly handle strings like "aabbaa" where characters repeat
  • Case sensitivity: "Aa" and "aa" are treated as different, so "Aa" is not a palindrome
  • Non-alphabetic characters: Special characters and numbers must be handled consistently

Boundary Conditions

  • Null input: Should handle null gracefully (typically return false or throw appropriate exception)
  • Very large strings: Performance considerations for very long strings (though O(n) is optimal)
  • Unicode characters: Multi-byte characters must be handled correctly when comparing positions

Common Implementation Mistakes

  • Off-by-one errors: Incorrectly checking bounds when comparing characters from both ends
  • Not accounting for case differences: Failing to normalize case before comparison
  • Ignoring whitespace: Including spaces in palindrome checks when they should be ignored (this depends on problem requirements)
  • Inefficient character access: Using charAt() in a loop without caching length, or using string concatenation instead of character arrays
  • Mutating input: Modifying the original string when it should remain immutable

Specific Pitfall Examples

// Mistake: Not handling case properly
assert !isPalindrome("Aa"); // This should be false, not true

// Mistake: Off-by-one in loop
// Loop condition should be i < length/2, not i <= length/2

// Mistake: Not handling null input
// isPalindrome(null) should be handled explicitly

9. Alternative Approaches

There are several alternative approaches to solving the palindrome check problem, each with different trade-offs:

1. Two-Pointer Approach (Optimal) The most efficient approach uses two pointers starting from both ends, moving toward center. This requires O(1) extra space and O(n) time complexity.

2. String Reversal Comparison Reverse the string and compare with original. This requires O(n) extra space for the reversed string but is conceptually simple.

// Alternative approach - String reversal comparison
public boolean isPalindrome(String s) {
    StringBuilder reversed = new StringBuilder(s);
    return s.equals(reversed.reverse().toString());
}

3. Recursive Approach Check if first and last characters match, then recursively check the substring. This uses O(n) space due to call stack.

// Alternative approach - Recursive
public boolean isPalindrome(String s) {
    return isPalindromeHelper(s, 0, s.length() - 1);
}

private boolean isPalindromeHelper(String s, int left, int right) {
    if (left >= right) return true;
    if (s.charAt(left) != s.charAt(right)) return false;
    return isPalindromeHelper(s, left + 1, right - 1);
}

4. Stack-Based Approach Push characters to stack and pop them while comparing with original string. This uses O(n) extra space.

The chosen two-pointer approach is superior because it has optimal time complexity O(n) with minimal space usage O(1), while alternatives either use more space or have less intuitive logic flow for this specific problem.

The stack and recursive approaches are less efficient due to additional space overhead, while the string reversal approach is simpler but requires O(n) extra memory for the reversed copy. The two-pointer method is the most elegant and efficient solution for this canonical problem.

10. Test Cases

public class PalindromeTestCases {
    public static void main(String[] args) {
        // Test case 1: Basic palindrome
        assert isPalindrome("racecar") == true;
        
        // Test case 2: Non-palindrome
        assert isPalindrome("hello") == false;
        
        // Test case 3: Single character
        assert isPalindrome("a") == true;
        
        // Test case 4: Empty string
        assert isPalindrome("") == true;
        
        // Test case 5: Case insensitive
        assert isPalindrome("RaceCar") == true;
        
        // Test case 6: With spaces and punctuation
        assert isPalindrome("A man a plan a canal Panama") == true;
        
        // Test case 7: Mixed case with spaces
        assert isPalindrome("Madam, I'm Adam") == true;
        
        // Test case 8: Even length palindrome
        assert isPalindrome("abccba") == true;
        
        // Test case 9: Even length non-palindrome
        assert isPalindrome("abcdba") == false;
        
        // Test case 10: Large palindrome
        String largePalindrome = "abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba";
        assert isPalindrome(largePalindrome) == true;
        
        System.out.println("All test cases passed!");
    }
    
    // Helper method for testing
    private static boolean isPalindrome(String s) {
        // Implementation would go here
        return true;
    }
}

The test cases cover:

  1. Basic palindrome strings
  2. Non-palindromes to verify negative detection
  3. Edge cases like single character and empty string
  4. Case insensitivity handling
  5. Strings with spaces and punctuation (real-world scenario)
  6. Mixed case variations that should still be recognized as palindromes
  7. Even-length palindromes vs non-palindromes
  8. Large input to test performance characteristics

Each test case is designed to validate a specific aspect of palindrome detection logic, ensuring robust handling of various input formats and edge conditions.

11. Final Summary

The core idea is to use the two-pointer technique to efficiently check if a string is a palindrome by comparing characters from both ends moving inward, which achieves optimal O(n) time and O(1) space complexity. This approach works because we only need to verify symmetry around the center, making it both time and space efficient. The solution handles all edge cases including empty strings, single characters, and mixed case inputs while maintaining clean, readable code structure suitable for production environments.

Was this article helpful?

Topics in this article

More from DSA Patterns

  • DSA Patterns

    Subsets and Permutations Problem Solving Patterns in Java

    Comprehensive guide to subset and permutation generation algorithms with iterative and recursive approaches, including time complexity analysis and practical implementation strategies.

  • DSA Problems

    Same Tree and Symmetric Tree Problems in Java

    Comprehensive Java solution for same tree and symmetric tree problems including recursive and iterative approaches with time and space complexity analysis.

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.