
AkhileshFrom character manipulation to advanced string algorithms - your definitive guide String...
String manipulation is a core skill that appears in 30% of coding interviews. This comprehensive guide covers 20 essential problems with clear explanations, multiple approaches, and practical patterns you'll use repeatedly.
Why String Problems Matter
String problems test your ability to:
Core Concepts
String Immutability in JavaScript
let str = "hello";
str[0] = "H"; // ❌ Doesn't work!
console.log(str); // Still "hello"
// Must create new string
str = "H" + str.slice(1); // ✅ "Hello"
Impact on complexity:
// ❌ O(n²) - Creates new string each iteration
let result = "";
for (let char of str) {
result += char; // New string created each time!
}
// ✅ O(n) - Build array then join once
let arr = [];
for (let char of str) {
arr.push(char);
}
let result = arr.join('');
Character Code Operations
// Check character type
const isUpperCase = char => char >= 'A' && char <= 'Z';
const isLowerCase = char => char >= 'a' && char <= 'z';
const isDigit = char => char >= '0' && char <= '9';
// Convert case
char.toLowerCase();
char.toUpperCase();
// Character codes
'A'.charCodeAt(0); // 65
'a'.charCodeAt(0); // 97
String.fromCharCode(65); // 'A'
// Distance between characters
'c'.charCodeAt(0) - 'a'.charCodeAt(0); // 2
Essential Patterns
function getFrequency(str) {
const freq = {};
for (let char of str) {
freq[char] = (freq[char] || 0) + 1;
}
return freq;
}
function twoPointerString(s) {
let left = 0, right = s.length - 1;
while (left < right) {
// Compare or process
left++;
right--;
}
}
function slidingWindow(s, k) {
let windowStart = 0;
for (let windowEnd = 0; windowEnd < s.length; windowEnd++) {
// Expand window
if (windowEnd - windowStart + 1 > k) {
windowStart++; // Shrink window
}
}
}
Problem Solutions
Problem 1: Valid Anagram
Difficulty: Easy | Platform: LeetCode #242
Problem:
Given two strings, return true if they are anagrams (same characters, different order).
Input: s = "anagram", t = "nagaram"
Output: true
Input: s = "rat", t = "car"
Output: false
Approach 1: Sorting
function isAnagram(s, t) {
if (s.length !== t.length) return false;
const sorted1 = s.split('').sort().join('');
const sorted2 = t.split('').sort().join('');
return sorted1 === sorted2;
}
// Time: O(n log n)
// Space: O(n)
Approach 2: Frequency Counter (Optimal)
function isAnagram(s, t) {
if (s.length !== t.length) return false;
const charCount = {};
for (let char of s) {
charCount[char] = (charCount[char] || 0) + 1;
}
for (let char of t) {
if (!charCount[char]) return false;
charCount[char]--;
}
return true;
}
// Time: O(n)
// Space: O(1) - At most 26 letters
Pattern: Frequency counting for character comparison
Problem 2: Valid Palindrome
Difficulty: Easy | Platform: LeetCode #125
Problem:
Check if string is palindrome considering only alphanumeric characters, ignoring case.
Input: s = "A man, a plan, a canal: Panama"
Output: true
Input: s = "race a car"
Output: false
Approach 1: Clean and Reverse
function isPalindrome(s) {
const cleaned = s.toLowerCase().replace(/[^a-z0-9]/g, '');
const reversed = cleaned.split('').reverse().join('');
return cleaned === reversed;
}
// Time: O(n)
// Space: O(n) - New strings created
Approach 2: Two Pointers (Optimal)
function isPalindrome(s) {
let left = 0, right = s.length - 1;
while (left < right) {
// Skip non-alphanumeric from left
while (left < right && !isAlphanumeric(s[left])) {
left++;
}
// Skip non-alphanumeric from right
while (left < right && !isAlphanumeric(s[right])) {
right--;
}
if (s[left].toLowerCase() !== s[right].toLowerCase()) {
return false;
}
left++;
right--;
}
return true;
}
function isAlphanumeric(char) {
return /[a-zA-Z0-9]/.test(char);
}
// Time: O(n)
// Space: O(1)
Pattern: Two pointers for in-place comparison
Problem 3: First Unique Character
Difficulty: Easy | Platform: LeetCode #387
Problem:
Find first non-repeating character in string.
Input: s = "leetcode"
Output: 0
Input: s = "loveleetcode"
Output: 2
Input: s = "aabb"
Output: -1
Solution: Two-Pass Frequency
function firstUniqChar(s) {
const freq = {};
// Pass 1: Count frequencies
for (let char of s) {
freq[char] = (freq[char] || 0) + 1;
}
// Pass 2: Find first with count 1
for (let i = 0; i < s.length; i++) {
if (freq[s[i]] === 1) {
return i;
}
}
return -1;
}
// Time: O(n)
// Space: O(1) - At most 26 letters
Pattern: Two-pass frequency analysis
Problem 4: Longest Common Prefix
Difficulty: Easy | Platform: LeetCode #14
Problem:
Find longest common prefix string amongst array of strings.
Input: strs = ["flower","flow","flight"]
Output: "fl"
Input: strs = ["dog","racecar","car"]
Output: ""
Approach 1: Horizontal Scanning
function longestCommonPrefix(strs) {
if (!strs.length) return "";
let prefix = strs[0];
for (let i = 1; i < strs.length; i++) {
while (strs[i].indexOf(prefix) !== 0) {
prefix = prefix.slice(0, -1);
if (!prefix) return "";
}
}
return prefix;
}
// Time: O(S) where S is sum of all characters
Approach 2: Vertical Scanning (Better)
function longestCommonPrefix(strs) {
if (!strs.length) return "";
for (let i = 0; i < strs[0].length; i++) {
const char = strs[0][i];
for (let j = 1; j < strs.length; j++) {
if (i === strs[j].length || strs[j][i] !== char) {
return strs[0].slice(0, i);
}
}
}
return strs[0];
}
// Time: O(S)
// Space: O(1)
Pattern: Character-by-character comparison
Problem 5: Group Anagrams
Difficulty: Medium | Platform: LeetCode #49
Problem:
Group strings that are anagrams together.
Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["eat","tea","ate"],["tan","nat"],["bat"]]
Approach 1: Sort as Key
function groupAnagrams(strs) {
const map = new Map();
for (let str of strs) {
const key = str.split('').sort().join('');
if (!map.has(key)) {
map.set(key, []);
}
map.get(key).push(str);
}
return Array.from(map.values());
}
// Time: O(n × k log k) where k is max string length
// Space: O(n × k)
Approach 2: Character Count as Key (Optimal)
function groupAnagrams(strs) {
const map = new Map();
for (let str of strs) {
const count = new Array(26).fill(0);
for (let char of str) {
count[char.charCodeAt(0) - 97]++;
}
const key = count.join('#');
if (!map.has(key)) {
map.set(key, []);
}
map.get(key).push(str);
}
return Array.from(map.values());
}
// Time: O(n × k)
// Space: O(n × k)
Pattern: Creating unique signatures for grouping
Problem 6: String Compression
Difficulty: Medium | Platform: LeetCode #443
Problem:
Compress string in-place. Return new length.
Input: chars = ["a","a","b","b","c","c","c"]
Output: 6, chars = ["a","2","b","2","c","3"]
Solution: Two Pointers
function compress(chars) {
let write = 0;
let read = 0;
while (read < chars.length) {
const char = chars[read];
let count = 0;
// Count consecutive occurrences
while (read < chars.length && chars[read] === char) {
read++;
count++;
}
// Write character
chars[write++] = char;
// Write count if > 1
if (count > 1) {
for (let digit of count.toString()) {
chars[write++] = digit;
}
}
}
return write;
}
// Time: O(n)
// Space: O(1)
Pattern: Two-pointer in-place modification
Problem 7: Reverse Words in String III
Difficulty: Easy | Platform: LeetCode #557
Problem:
Reverse characters in each word while preserving order.
Input: s = "Let's take LeetCode contest"
Output: "s'teL ekat edoCteeL tsetnoc"
Solution: Split-Reverse-Join
function reverseWords(s) {
return s.split(' ')
.map(word => word.split('').reverse().join(''))
.join(' ');
}
// Time: O(n)
// Space: O(n)
Manual Approach:
function reverseWords(s) {
const words = s.split(' ');
for (let i = 0; i < words.length; i++) {
words[i] = reverseString(words[i]);
}
return words.join(' ');
}
function reverseString(str) {
let arr = str.split('');
let left = 0, right = arr.length - 1;
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
return arr.join('');
}
Pattern: Process independent parts separately
Problem 8: Caesar Cipher (HackerRank)
Problem:
Shift each letter by k positions in alphabet.
Input: s = "abc", k = 2
Output: "cde"
Input: s = "xyz", k = 2
Output: "zab" // Wraps around
Solution: Modulo Arithmetic
function caesarCipher(s, k) {
let result = '';
for (let char of s) {
if (char >= 'a' && char <= 'z') {
const shifted = ((char.charCodeAt(0) - 97 + k) % 26);
result += String.fromCharCode(shifted + 97);
}
else if (char >= 'A' && char <= 'Z') {
const shifted = ((char.charCodeAt(0) - 65 + k) % 26);
result += String.fromCharCode(shifted + 65);
}
else {
result += char;
}
}
return result;
}
// Time: O(n)
// Space: O(n)
Pattern: Modulo for cyclic operations
Problem 9: Palindrome Index (HackerRank)
Problem:
Find index of character to remove to make palindrome.
Input: s = "aaab"
Output: 3 // Remove 'b'
Input: s = "baa"
Output: 0 // Remove first 'b'
Input: s = "aaa"
Output: -1 // Already palindrome
Solution: Two Pointers with Testing
function palindromeIndex(s) {
let left = 0, right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) {
// Try removing left
if (isPalindrome(s, left + 1, right)) {
return left;
}
// Try removing right
if (isPalindrome(s, left, right - 1)) {
return right;
}
return -1;
}
left++;
right--;
}
return -1;
}
function isPalindrome(s, left, right) {
while (left < right) {
if (s[left] !== s[right]) return false;
left++;
right--;
}
return true;
}
// Time: O(n)
// Space: O(1)
Pattern: Two pointers with backtracking test
Problem 10: Alternating Characters (HackerRank)
Problem:
Count deletions needed to remove consecutive duplicates.
Input: s = "AAAA"
Output: 3
Input: s = "AAABBB"
Output: 4
Input: s = "ABABABAB"
Output: 0
Solution: Linear Scan
function alternatingCharacters(s) {
let deletions = 0;
for (let i = 1; i < s.length; i++) {
if (s[i] === s[i - 1]) {
deletions++;
}
}
return deletions;
}
// Time: O(n)
// Space: O(1)
Pattern: Adjacent comparison
LeetCode Easy
✅ Valid Anagram - #242
✅ First Unique Character - #387
✅ Valid Palindrome - #125
✅ Reverse String - #344
✅ Longest Common Prefix - #14
✅ Length of Last Word - #58
✅ Implement strStr() - #28
✅ Isomorphic Strings - #205
✅ Word Pattern - #290
✅ Reverse Words III - #557
LeetCode Medium
✅ String Compression - #443
✅ Group Anagrams - #49
HackerRank
✅ CamelCase
✅ Strong Password
✅ Two Characters
✅ Caesar Cipher
✅ Mars Exploration
✅ Funny String
✅ Gemstones
✅ Alternating Characters
✅ Palindrome Index
| Pattern | Identify When | Common Problems |
|---|---|---|
| Frequency Counter | Comparing character counts | Anagram, First Unique |
| Two Pointer | In-place palindrome check | Valid Palindrome |
| Sliding Window | Substring problems | Longest Substring |
| Split-Process-Join | Word-level operations | Reverse Words |
| Character Codes | Shifting, encoding | Caesar Cipher |
| HashMap Signature | Grouping similar strings | Group Anagrams |
Week 1: Foundations
Days 1-2:
Days 3-4:
Days 5-7:
Week 2: Advanced Patterns
Days 1-3:
Days 4-7:
Week 3: Speed & Mastery
Common Mistakes to Avoid
// ❌ Wrong
s[0] = 'A';
// ✅ Correct
s = 'A' + s.slice(1);
// ❌ O(n²)
let result = "";
for (let char of s) result += char;
// ✅ O(n)
let arr = [];
for (let char of s) arr.push(char);
result = arr.join('');
// Always clarify requirements
s.toLowerCase(); // When needed
if (!s || s.length === 0) return "";
if (s.length === 1) return s;
Quick Tips for Interviews
Ask about constraints:
Start simple:
Communicate:
Test thoroughly: