Skip to main content

Command Palette

Search for a command to run...

Journal: Valid Palindrome, Two Sum II, CQRS vs Read Replica

Published
6 min read
R

Full-stack Engineer specializing in Node.js, Nest.js, MERN. Expert in building scalable APIs & real-time apps. Focus on clean code, Security, and performance.

This article is part of my journal. Read more

LeetCode 125 — Valid Palindrome

Today I worked on LeetCode 125, the Valid Palindrome problem. The task is to check if a string is a palindrome after converting all uppercase letters to lowercase and keeping only alphanumeric characters.

My first instinct was to reverse the string and compare, but I quickly realized that approach would create problems because of special characters mixed in between. So instead, I decided to initialize a variable to keep track of the valid alphanumeric characters in straight order, then reverse it and check if both are equal.

I looped over every character, lowercased it first, checked if it was alphanumeric using a regex range (0–9 and a–z), and only then appended it to my string variable. The code was working. Then I moved the lowercase step upfront — before the loop — that made the code cleaner and a bit faster.

It was working, but only 10% of users ranked below me, which told me my solution was not optimal. One edge case tripped me up: I was using \w in my regex which includes underscores. I fixed that by manually defining the alphanumeric range as 0-9 and a-z. Since I had already lowercased the string, I didn't need to include capital letters separately. After that fix, it passed all test cases.

Here is the final code

function isPalindrome(s: string): boolean {
    let reverseStr = ''
    let str = ''
    for (let char of s) {
        const lowerCase = char.toLowerCase()
        if (/[0-9a-z]/.test(lowerCase)) {
            str += lowerCase
            reverseStr = lowerCase + reverseStr
        }
    }

    return str === reverseStr
};

Optimizing the Approach

Rather than creating a reversed copy of the string, I explored a two-pointer approach. The idea: lowercase the whole string once before the loop, replace all non-alphanumeric characters with an empty string, then use a left pointer starting at 0 and a right pointer starting at the last index. Loop while left < right, and if the leftmost character does not equal the rightmost character, return false. Otherwise, increment left and decrement right.

One thing I learned along the way — in regex, using a caret (^) inside brackets means "does not match this range." That made it easy to strip out non-alphanumeric characters in one line.

I ran it and the runtime percentage jumped around — 20% one run, higher another. I concluded that comparing runtime percentages on LeetCode is not very reliable since results vary each run.

Here is the final code

function isPalindrome(s: string): boolean {
    s = s.toLowerCase().replace(/[^0-9a-z]/g, '')

    let left = 0
    let right = s.length - 1
    while (left < right) {
        if (s[left] != s[right]) return false
        left++
        right--
    }
    return true
};

LeetCode 167 — Two Sum II (Input Array Is Sorted)

Next I moved on to LeetCode 167. Given a sorted integer array in non-decreasing order, I needed to find two numbers that add up to a specific target and return their indices.

I used the two-pointer pattern. Left starts at index 0, right starts at the last index (numbers.length - 1). I loop while left < right, compute the sum of the two pointed values, and check against the target. If the sum equals the target, I return the indices. If the sum is greater than the target, I decrement right. If the sum is smaller, I increment left.

My first test case failed because I forgot the question uses 1-based indexing. The answer needed to be [1, 2] instead of [0, 1]. Once I added 1 to both indices before returning, it passed.

Here is the final code

function twoSum(numbers: number[], target: number): number[] {
    let left = 0
    let right = numbers.length - 1

    while (left < right) {
        const sum = numbers[left] + numbers[right]
        if (sum === target) return [left + 1, right + 1]
        else if (sum > target) {
            right--
        } else {
            left++
        }
    }
    return []
};

Career Goals & Personal Project

I've been thinking about switching companies and targeting a top-tier company in Nepal. To prepare, I'm focused on three areas: refreshing my memory on DSA, design patterns, and advanced system design for a senior position.

I also realized I don't have a solid personal project to make an impression, so I've decided to build one. The idea is a budget management system — a combination of Splitwise and a money tracker app, so users can track both personal expenses and group expenses in the same place. I'm planning to use PostgreSQL as the database since I haven't used SQL based database in real world project. This project will give me the confidence that I need to get my carrier into the next level.


AI & Generative AI Exploration

I want to gain more knowledge in AI, so I've been going through AI content on YouTube. First priority is setting up a proper development environment for coding to improve productivity.

I also came across a repository from Karpathy — one of the founders of OpenAI — called microGPT. It shows how the core logic inside GenAI works, and the entire core codebase is only 200 lines of code. I'm going to dig into it to understand how AI works internally and strengthen my knowledge of generative AI.

I also found a video by Alex Jinskind where he talks about Claude Code and agentic coding — definitely going to watch that too.


System Design — CQRS vs Read Replica

I watched a video by Core Opinion that cleared up a confusion I didn't even know I had: read replica is not the same as CQRS.

CQRS (Command Query Responsibility Segregation) is a code-level pattern. When a feature involves expensive reads — say, multiple SQL joins — you create a separate read table where data is de-normalized and pre-computed. Write queries go to the original table. Read queries pull from the optimized read table. Many developers are probably already doing this without knowing it has a name.

Read replica, on the other hand, is an infrastructure-level solution. You use it when you're hitting actual traffic and scaling issues at the database infrastructure level — not just slow queries in code.

The distinction is clearer now: CQRS is a code optimization, read replica is an infrastructure scaling strategy.


End of day. Good progress on DSA, a clearer project direction, and a useful system design concept locked in.

21 views

More from this blog

R

Raman Karki Blogs | Full Stack Developer

15 posts

Passionate Full Stack Web Developer | Self Learner | Blog's on Personal Story, Full Stack Web Development, and related fields