Leetcode/Guide/Strings
Last edited by dave on 08/11/2025, 16:24:30 UTC
Contents
This guide focuses on practical patterns and reusable functions for common string interview problems.
All examples avoid classes, use descriptive variable names, and rely on for/while where helpful.
🧠Core Facts You’ll Use Constantly
- Strings are immutable in JS. To “modify,” build a new string (or work with arrays).
- Indexing:
text[i]andtext.charCodeAt(i)are O(1). - Case handling: normalize early with
.toLowerCase()/.toUpperCase(). - Trim & split:
text.trim(),text.split(' ')(be careful with multiple spaces—use regex if needed). - Join:
array.join('')to rebuild strings efficiently. - Two pointers & sliding window work great for substring constraints.
Handy Helpers
Character Frequency Map (ASCII-ish)
function buildFrequencyMapLowercaseLetters(inputText) { const frequencyMap = new Array(26).fill(0); for (let index = 0; index < inputText.length; index++) { const code = inputText.charCodeAt(index) - 97; // 'a' → 97 if (code >= 0 && code < 26) frequencyMap[code]++; } return frequencyMap; }
Generic Frequency (Object)
function buildFrequencyMapGeneric(inputText) { const frequencyMap = Object.create(null); for (let index = 0; index < inputText.length; index++) { const character = inputText[index]; frequencyMap[character] = (frequencyMap[character] || 0) + 1; } return frequencyMap; }
Classic Problems & Patterns
1) Reverse a String (Two Pointers)
function reverseStringCharacters(inputText) { const characters = inputText.split(''); for ( let leftIndex = 0, rightIndex = characters.length - 1; leftIndex < rightIndex; leftIndex++, rightIndex-- ) { const temporaryCharacter = characters[leftIndex]; characters[leftIndex] = characters[rightIndex]; characters[rightIndex] = temporaryCharacter; } return characters.join(''); } // Demo console.log(reverseStringCharacters("hello")); // "olleh"
2) Palindrome Check (Ignore Non-Alphanumerics, Case-Insensitive)
function isCleanPalindrome(inputText) { let leftIndex = 0; let rightIndex = inputText.length - 1; while (leftIndex < rightIndex) { while (leftIndex < rightIndex && !isAlphaNumeric(inputText[leftIndex])) leftIndex++; while (leftIndex < rightIndex && !isAlphaNumeric(inputText[rightIndex])) rightIndex--; if (inputText[leftIndex].toLowerCase() !== inputText[rightIndex].toLowerCase()) { return false; } leftIndex++; rightIndex--; } return true; } function isAlphaNumeric(character) { const code = character.charCodeAt(0); const isDigit = code >= 48 && code <= 57; const isUpper = code >= 65 && code <= 90; const isLower = code >= 97 && code <= 122; return isDigit || isUpper || isLower; } // Demo console.log(isCleanPalindrome("A man, a plan, a canal: Panama")); // true
3) Anagram Check (Two Strings)
function areAnagrams(textA, textB) { if (textA.length !== textB.length) return false; const frequencyMap = Object.create(null); for (let i = 0; i < textA.length; i++) { const charA = textA[i].toLowerCase(); frequencyMap[charA] = (frequencyMap[charA] || 0) + 1; } for (let j = 0; j < textB.length; j++) { const charB = textB[j].toLowerCase(); if (!frequencyMap[charB]) return false; frequencyMap[charB]--; } return true; } // Demo console.log(areAnagrams("Listen", "Silent")); // true
4) Find All Anagram Indices (Sliding Window)
function findAllAnagramStartIndices(sourceText, patternText) { if (patternText.length > sourceText.length) return []; const needed = Object.create(null); for (let i = 0; i < patternText.length; i++) { const c = patternText[i].toLowerCase(); needed[c] = (needed[c] || 0) + 1; } const resultIndices = []; let matchedUniqueCharacters = 0; const totalUniqueNeeded = Object.keys(needed).length; let windowStartIndex = 0; const windowCounts = Object.create(null); for (let windowEndIndex = 0; windowEndIndex < sourceText.length; windowEndIndex++) { const endChar = sourceText[windowEndIndex].toLowerCase(); windowCounts[endChar] = (windowCounts[endChar] || 0) + 1; if (needed[endChar] && windowCounts[endChar] === needed[endChar]) { matchedUniqueCharacters++; } // shrink when window size exceeds pattern length while (windowEndIndex - windowStartIndex + 1 > patternText.length) { const startChar = sourceText[windowStartIndex].toLowerCase(); if (needed[startChar] && windowCounts[startChar] === needed[startChar]) { matchedUniqueCharacters--; } windowCounts[startChar]--; windowStartIndex++; } if (matchedUniqueCharacters === totalUniqueNeeded && (windowEndIndex - windowStartIndex + 1) === patternText.length) { resultIndices.push(windowStartIndex); } } return resultIndices; } // Demo console.log(findAllAnagramStartIndices("cbaebabacd", "abc")); // [0, 6]
5) Longest Substring Without Repeating Characters (Sliding Window)
function findLengthOfLongestSubstringWithoutRepeats(inputText) { let windowStartIndex = 0; let longestLengthFound = 0; const lastSeenIndexOf = Object.create(null); for (let windowEndIndex = 0; windowEndIndex < inputText.length; windowEndIndex++) { const currentCharacter = inputText[windowEndIndex]; if (lastSeenIndexOf[currentCharacter] !== undefined && lastSeenIndexOf[currentCharacter] >= windowStartIndex) { windowStartIndex = lastSeenIndexOf[currentCharacter] + 1; } lastSeenIndexOf[currentCharacter] = windowEndIndex; const currentWindowLength = windowEndIndex - windowStartIndex + 1; if (currentWindowLength > longestLengthFound) { longestLengthFound = currentWindowLength; } } return longestLengthFound; } // Demo console.log(findLengthOfLongestSubstringWithoutRepeats("abcabcbb")); // 3
6) Longest Repeating Character Replacement (At Most k Changes)
function findLongestSubstringAfterKReplacements(inputText, maxReplacements) { let windowStartIndex = 0; let maxSameCharCountInWindow = 0; let longestLengthFound = 0; const frequencyMap = Object.create(null); for (let windowEndIndex = 0; windowEndIndex < inputText.length; windowEndIndex++) { const endChar = inputText[windowEndIndex]; frequencyMap[endChar] = (frequencyMap[endChar] || 0) + 1; if (frequencyMap[endChar] > maxSameCharCountInWindow) { maxSameCharCountInWindow = frequencyMap[endChar]; } while ((windowEndIndex - windowStartIndex + 1) - maxSameCharCountInWindow > maxReplacements) { const startChar = inputText[windowStartIndex]; frequencyMap[startChar]--; windowStartIndex++; } const currentWindowLength = windowEndIndex - windowStartIndex + 1; if (currentWindowLength > longestLengthFound) { longestLengthFound = currentWindowLength; } } return longestLengthFound; } // Demo console.log(findLongestSubstringAfterKReplacements("AABABBA", 1)); // 4
7) String Compression (Run-Length Encoding)
function runLengthEncode(inputText) { if (inputText.length === 0) return ""; let resultBuilder = []; let runCount = 1; for (let index = 1; index <= inputText.length; index++) { if (inputText[index] === inputText[index - 1]) { runCount++; } else { resultBuilder.push(inputText[index - 1] + String(runCount)); runCount = 1; } } return resultBuilder.join(''); } // Demo console.log(runLengthEncode("aaabbccccd")); // "a3b2c4d1"
8) Valid Parentheses (Stack on String)
function isValidParenthesesSequence(inputText) { const stack = []; const matching = { ')': '(', ']': '[', '}': '{' }; const openers = new Set(['(', '[', '{']); for (let index = 0; index < inputText.length; index++) { const ch = inputText[index]; if (openers.has(ch)) { stack.push(ch); } else if (matching[ch]) { if (stack.length === 0 || stack[stack.length - 1] !== matching[ch]) return false; stack.pop(); } } return stack.length === 0; } // Demo console.log(isValidParenthesesSequence("({[]})")); // true
9) Check Isomorphic Strings
function areIsomorphicStrings(textA, textB) { if (textA.length !== textB.length) return false; const mapAtoB = Object.create(null); const mapBtoA = Object.create(null); for (let i = 0; i < textA.length; i++) { const a = textA[i], b = textB[i]; if ((mapAtoB[a] && mapAtoB[a] !== b) || (mapBtoA[b] && mapBtoA[b] !== a)) { return false; } mapAtoB[a] = b; mapBtoA[b] = a; } return true; } // Demo console.log(areIsomorphicStrings("egg", "add")); // true console.log(areIsomorphicStrings("foo", "bar")); // false
10) Longest Palindromic Substring (Expand Around Center)
function findLongestPalindromicSubstring(inputText) { if (inputText.length < 2) return inputText; let bestStartIndex = 0; let bestLength = 1; function expandFromCenter(leftIndex, rightIndex) { while (leftIndex >= 0 && rightIndex < inputText.length && inputText[leftIndex] === inputText[rightIndex]) { leftIndex--; rightIndex++; } // now [leftIndex+1, rightIndex-1] is palindrome const currentStart = leftIndex + 1; const currentLength = rightIndex - leftIndex - 1; if (currentLength > bestLength) { bestLength = currentLength; bestStartIndex = currentStart; } } for (let center = 0; center < inputText.length; center++) { expandFromCenter(center, center); // odd length expandFromCenter(center, center + 1); // even length } return inputText.slice(bestStartIndex, bestStartIndex + bestLength); } // Demo console.log(findLongestPalindromicSubstring("babad")); // "bab" or "aba"
Quick Tactics Checklist
- Normalize early: lowercase/trim/strip non-alphanumerics if the spec allows.
- Two pointers: palindrome, reverse, comparing ends.
- Sliding window: “longest/shortest substring with constraint”.
- Frequency maps: anagrams, composition checks.
- Stacks: parentheses/bracket validation, decoding nested patterns.
- Center expansion: palindromes.
- Build with arrays: push chars and
.join('')(faster than string concatenation in tight loops).
Complexity Cheats
| Pattern | Typical Time | Typical Space |
|---|---|---|
| Two Pointers | O(n) | O(1) |
| Sliding Window | O(n) | O(k) (map) |
| Frequency Map | O(n) | O(ÎŁ) (alphabet size) |
| Stack (parentheses) | O(n) | O(n) |
| Center Expand (palindrome) | O(n²) worst | O(1) |
“Gotchas” to Remember
- String immutability → prefer arrays for heavy edits.
- Unicode edge cases (emoji/surrogates) can break
length/indexing; most coding tests ignore this, but note it. - Regex splits:
text.trim().split(/\s+/)handles multiple spaces.
Mini Template for New Problems
function solveStringProblem(inputText) { // 1) Normalize if allowed // const normalizedText = inputText.toLowerCase().replace(/[^a-z0-9]/g, ''); // 2) Pick a pattern: two pointers / sliding window / stack / frequency map // 3) Write small helpers if needed (e.g., frequency map) // 4) Implement with clear variables and for/while loops // return resultValue; }
Use these snippets as building blocks, adapt variable names to be explicit, and you’ll move faster in string-heavy coding tests. Good luck! 🚀
Backlinks (1)
- General
No backlinks yet.
- User
- Redirects
No backlinks yet.
- Media
No backlinks yet.
- Categories
No backlinks yet.
Categories (0)
No categories assigned to this page.
Edit Level
> Signed In Users
Latest on Lounge
Join the conversation about the 'Leetcode/Guide/Strings' article →
No comments yet. Be the first to comment!