DEV Community

Cover image for Master Strings in 20 Problems: Complete Pattern Guide
Akhilesh
Akhilesh

Posted on

Master Strings in 20 Problems: Complete Pattern Guide

From character manipulation to advanced string algorithms - your definitive guide

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:

  • Manipulate immutable data - Understanding string behavior
  • Optimize character operations - When to convert to arrays
  • Handle edge cases - Empty strings, special characters, Unicode
  • Apply efficient algorithms - Two pointers, sliding windows, hashing

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"
Enter fullscreen mode Exit fullscreen mode

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('');
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Essential Patterns

  1. Frequency Counter
function getFrequency(str) {
    const freq = {};
    for (let char of str) {
        freq[char] = (freq[char] || 0) + 1;
    }
    return freq;
}
Enter fullscreen mode Exit fullscreen mode
  1. Two Pointer
function twoPointerString(s) {
    let left = 0, right = s.length - 1;
    while (left < right) {
        // Compare or process
        left++;
        right--;
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Sliding Window
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
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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: ""
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"]]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Solution: Split-Reverse-Join

function reverseWords(s) {
    return s.split(' ')
            .map(word => word.split('').reverse().join(''))
            .join(' ');
}

// Time: O(n)
// Space: O(n)
Enter fullscreen mode Exit fullscreen mode

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('');
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Pattern: Adjacent comparison

Complete Problem List

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 Recognition Guide

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

Practice Strategy

Week 1: Foundations
Days 1-2:

  • Valid Anagram
  • Valid Palindrome
  • Master frequency counting and two pointers

Days 3-4:

  • First Unique Character
  • Longest Common Prefix
  • Practice two-pass techniques

Days 5-7:

  • String Compression
  • Reverse Words
  • All Easy LeetCode problems

Week 2: Advanced Patterns
Days 1-3:

  • Group Anagrams
  • Palindrome Index
  • Master signature creation

Days 4-7:

  • Caesar Cipher
  • Alternating Characters
  • All HackerRank problems

Week 3: Speed & Mastery

  • Solve each problem in < 20 minutes
  • Practice explaining approach
  • Focus on edge cases

Common Mistakes to Avoid

  1. String Immutability
// ❌ Wrong
s[0] = 'A';
Enter fullscreen mode Exit fullscreen mode

// ✅ Correct
s = 'A' + s.slice(1);

  1. Inefficient Concatenation
// ❌ 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('');
Enter fullscreen mode Exit fullscreen mode
  1. Case Sensitivity
// Always clarify requirements
s.toLowerCase(); // When needed
Enter fullscreen mode Exit fullscreen mode
  1. Edge Cases
if (!s || s.length === 0) return "";
if (s.length === 1) return s;
Enter fullscreen mode Exit fullscreen mode

Quick Tips for Interviews

Ask about constraints:

  • Case sensitive?
  • Special characters allowed?
  • ASCII or Unicode?

Start simple:

  • Explain brute force first
  • Then optimize

Communicate:

  • "I'll use a frequency map because..."
  • "Two pointers work here because..."

Test thoroughly:

  • Empty string
  • Single character
  • All same characters
  • Mixed case

Top comments (0)