Palindrome Checker
The Problem
Given a string, determine whether it is a palindrome — a word or phrase that reads the same forwards and backwards.
Input: s = "racecar" → Output: True
Input: s = "hello" → Output: False
Examples
Example 1
Input: "racecar"
Output: True
Explanation: r-a-c-e-c-a-r reversed is still r-a-c-e-c-a-r
Example 2
Input: "hello"
Output: False
Explanation: 'h' ≠ 'o' — the first and last characters already disagree
Example 3
Input: "abcba"
Output: True
Example 4 — single character
Input: "a"
Output: True (every single character is trivially a palindrome)
Intuition
A string is a palindrome when its first character equals its last, its second equals its second-to-last, and so on all the way to the middle.
That’s a mirror-pair relationship — exactly what two pointers are built for. Place left at the start and right at the end. At each step, compare s[left] and s[right]:
- If they match → the pair is fine, move both inward and continue
- If they don’t match → it’s not a palindrome, return
Falseimmediately - If
left >= rightwithout any mismatch → every pair matched, returnTrue
No extra memory needed. One pass.
---
config:
theme: base
themeVariables:
primaryColor: "#dbeafe"
primaryBorderColor: "#3b82f6"
primaryTextColor: "#1e3a5f"
lineColor: "#64748b"
secondaryColor: "#ede9fe"
tertiaryColor: "#fef9c3"
---
flowchart TB
subgraph S0["Start — left=0, right=6"]
direction LR
P0L(["left"]) --> R0["r"] --- A0["a"] --- C0["c"] --- E0["e"] --- C1["c"] --- A1["a"] --- R1["r"]
R1 --> P0R(["right"])
end
subgraph S1["'r'='r' ✓ left=1, right=5"]
direction LR
P1L(["left"]) --> A2["a"] --- C2["c"] --- E1["e"] --- C3["c"] --- A3["a"]
A3 --> P1R(["right"])
end
subgraph S2["'a'='a' ✓ left=2, right=4"]
direction LR
P2L(["left"]) --> C4["c"] --- E2["e"] --- C5["c"]
C5 --> P2R(["right"])
end
subgraph S3["'c'='c' ✓ left=3, right=3"]
direction LR
P3L(["left=right"]) --> E3["e"]
E3 --> P3R(["right=left"])
end
subgraph S4["left ≥ right → all pairs matched"]
DONE(["✓ return True"])
end
S0 -->|"match → move inward"| S1
S1 -->|"match → move inward"| S2
S2 -->|"match → move inward"| S3
S3 -->|"left ≥ right"| S4
Checking "racecar" for palindrome — every mirror pair matches; when pointers meet at the centre, the check passes.
Applying the Diagnostic Questions
| Check | Answer for Palindrome Checker |
|---|---|
| ✅ Two positions simultaneously? | Yes — s[left] and s[right] are compared together at every step |
| ✅ One near start, one near end? | Yes — left = 0, right = n-1 |
| ✅ Both move inward? | Yes — left++, right-- after every matching pair |
| ✅ Simple work at each step? | Yes — one comparison per iteration, return immediately on mismatch |
The structure is identical to Flip Characters — the only difference is the loop body: compare instead of swap.
Why check from both ends simultaneously? A palindrome’s definition is symmetric: the character at position i from the left must equal the character at position i from the right, for every i from 0 to n/2. Two pointers map this requirement directly — left and right track the pair at distance i from each end. Moving both inward covers every required pair in exactly n/2 steps.
What breaks if you use only one pointer? A single pointer could reverse the string and compare — but that costs O(n) extra space for the reversed copy and a second O(n) pass. Two pointers do it in one pass with O(1) space, and gain the early-exit advantage: as soon as any pair mismatches, False is returned without inspecting the rest. For a string like "abcde...xyz" + "XYZ", the mismatch at position 0 stops the algorithm immediately.
What Failure Looks Like
---
config:
theme: base
themeVariables:
primaryColor: "#dbeafe"
primaryBorderColor: "#3b82f6"
primaryTextColor: "#1e3a5f"
lineColor: "#64748b"
secondaryColor: "#ede9fe"
tertiaryColor: "#fef9c3"
---
flowchart LR
subgraph FAIL["Checking 'hello' — left=0, right=4"]
direction LR
FL(["left"]) --> H["h"] --- E["e"] --- L1["l"] --- L2["l"] --- O["o"]
O --> FR(["right"])
end
CMP{"'h' = 'o' ?"}
NO(["✗ return False"])
FAIL --> CMP
CMP -->|"No"| NO
The check fails immediately on the first pair — 'h' ≠ 'o' is enough to return False without looking at the rest.
This early-exit property makes two-pointer palindrome checking efficient in practice — you never process more pairs than necessary.
Solution
class Solution:
def is_palindrome(self, s: str) -> bool:
left = 0
right = len(s) - 1
while left < right:
if s[left] != s[right]:
return False # mismatch found — not a palindrome
left += 1
right -= 1
return True # all mirror pairs matched
# --- Test ---
sol = Solution()
print(sol.is_palindrome("racecar")) # True
print(sol.is_palindrome("hello")) # False
print(sol.is_palindrome("abcba")) # True
print(sol.is_palindrome("a")) # True
print(sol.is_palindrome("ab")) # False
print(sol.is_palindrome("aa")) # True
Dry Run — “racecar”
s = "racecar", n = 7
| Iteration | left | right | s[left] | s[right] | Match? |
|---|---|---|---|---|---|
| 1 | 0 | 6 | 'r' | 'r' | ✅ |
| 2 | 1 | 5 | 'a' | 'a' | ✅ |
| 3 | 2 | 4 | 'c' | 'c' | ✅ |
| — | 3 | 3 | — | — | left ≥ right → stop |
Return True ✓
Dry Run — “hello”
| Iteration | left | right | s[left] | s[right] | Match? |
|---|---|---|---|---|---|
| 1 | 0 | 4 | 'h' | 'o' | ❌ → return False immediately |
Return False ✓
Complexity Analysis
| Complexity | Reasoning | |
|---|---|---|
| Time | O(n) worst case | Every mirror pair checked once if all match; exits early on first mismatch |
| Space | O(1) | Only two pointer variables |
Edge Cases
| Scenario | Input | Output | Note |
|---|---|---|---|
| Empty string | "" | True | left = 0 > right = -1 — loop never runs, vacuously true |
| Single character | "a" | True | left = right — loop never runs |
| Two identical chars | "aa" | True | One comparison, both match |
| Two different chars | "ab" | False | One comparison, immediate mismatch |
| All same characters | "aaaa" | True | Every pair matches |
Key Takeaway
Palindrome checking is the comparison variant of the two-pointer pattern. Where “Flip Characters” swapped mirror pairs, here we compare them. The pointer movement is identical — the work in the loop body is the only difference. This is the pattern: same skeleton, swap the operation.