"S"

Strings

Sliding window, two pointers, frequency maps, and pattern matching on character sequences.

27 Problems3-4 hours11 Easy14 Medium2 Hard

Overview

Strings are arrays of characters. Most string problems map directly to array techniques. The most common patterns are sliding window for substring problems, frequency maps (character count arrays of size 26) for anagram detection, stack-based parsing, and expand-around-center for palindromes.

Complexity Summary

Time Complexity

Most sliding window solutions: O(n)

Space Complexity

O(1) with a fixed-size frequency array, O(n) otherwise

Key Patterns & Techniques

Learn the core patterns in this topic. Each block explains when to use the pattern, the intuition behind it, and a compact code example.

1

Frequency Map

Concept

Use this when the problem depends on how many times each character appears rather than where it appears

Why It Works

For lowercase English letters, a fixed array of size 26 is often faster and simpler than a hash map. For general Unicode or mixed-case input, a hash map is safer. This pattern is common in anagrams, permutation checks, and counting distinct characters.

Pattern Example
Problem

Problem Valid Anagram. Count every character in the first string, subtract counts using the second string, and verify every frequency returns to zero.

Implementation
function isAnagram(s, t) {
  if (s.length !== t.length) return false;
  const freq = {};
  for (const ch of s) freq[ch] = (freq[ch] || 0) + 1;
  for (const ch of t) {
    if (!freq[ch]) return false;
    freq[ch]--;
  }
  return true;
}
Practice questions for this pattern
2

Sliding Window on Characters

Concept

Use this when the question asks about a substring and the current answer depends only on the characters inside a moving range

Why It Works

Expand the right pointer to include new characters, and shrink from the left when the window becomes invalid. This avoids rebuilding character counts for every substring from scratch. It is one of the most important patterns for strings.

Pattern Example
Problem

Problem Longest Substring Without Repeating Characters. Expand the window to the right, and whenever a duplicate appears, move the left pointer until the duplicate is removed.

Implementation
function lengthOfLongestSubstring(s) {
  const seen = new Map();
  let left = 0, maxLen = 0;
  for (let right = 0; right < s.length; right++) {
    const ch = s[right];
    if (seen.has(ch) && seen.get(ch) >= left) left = seen.get(ch) + 1;
    seen.set(ch, right);
    maxLen = Math.max(maxLen, right - left + 1);
  }
  return maxLen;
}
Practice questions for this pattern
3

Stack-Based Parsing

Concept

Use this when the string contains nested structure, balanced symbols, or encoded expressions where later characters depend on unfinished earlier characters

Why It Works

A stack naturally stores incomplete context. This is the standard tool for valid parentheses, decoding strings, and expression parsing.

Pattern Example
Problem

Problem Valid Parentheses. Push opening brackets onto the stack. On a closing bracket, check whether it matches the most recent opening bracket.

Implementation
function validParentheses(s) {
  const stack = [];
  const pairs = { ')': '(', ']': '[', '}': '{' };
  for (const ch of s) {
    if (ch === '(' || ch === '[' || ch === '{') stack.push(ch);
    else if (stack.pop() !== pairs[ch]) return false;
  }
  return stack.length === 0;
}
Practice questions for this pattern
4

Expand Around Center

Concept

Use this when the problem asks for palindromic substrings

Why It Works

Every palindrome is centered either at one character for odd length or between two characters for even length. Instead of checking all substrings, expand only from valid centers. This reduces the brute-force O(n³) approach to O(n²) with very clean logic.

Pattern Example
Problem

Problem Longest Palindromic Substring. Try each index as an odd center and each gap as an even center, and keep the longest palindrome found.

Implementation
function longestPalindrome(s) {
  if (s.length < 2) return s;
  let best = s[0];
  const expand = (l, r) => {
    while (l >= 0 && r < s.length && s[l] === s[r]) {
      l--; r++;
    }
    return s.slice(l + 1, r);
  };
  for (let i = 0; i < s.length; i++) {
    const odd = expand(i, i);
    const even = expand(i, i + 1);
    if (odd.length > best.length) best = odd;
    if (even.length > best.length) best = even;
  }
  return best;
}
Practice questions for this pattern
5

Two Pointers for Validation

Concept

Use this when you need to compare characters from both ends, especially for palindrome-style checks or when skipping non-important characters

Why It Works

The idea is to move inward while maintaining an invariant about the substring between the pointers. This pattern is simple but appears often.

Pattern Example
Problem

Problem Valid Palindrome. Ignore non-alphanumeric characters, compare lowercase characters from both ends, and stop early on any mismatch.

Implementation
function groupAnagrams(strs) {
  const groups = new Map();
  for (const word of strs) {
    const key = word.split('').sort().join('');
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(word);
  }
  return Array.from(groups.values());
}
Practice questions for this pattern
6

Canonical Key Transformation

Concept

Use this when two strings should be treated as equivalent after reordering or normalization

Why It Works

Convert every string into a canonical key, then group or compare based on that key. For anagrams, sorting characters is the simplest canonicalization. This pattern turns grouping problems into map aggregation problems.

Pattern Example
Problem

Problem Group Anagrams. Sort each word, use the sorted string as the key, and collect all words sharing that key.

Implementation
function isPalindrome(s) {
  let left = 0, right = s.length - 1;
  while (left < right) {
    while (left < right && !/[a-z0-9]/i.test(s[left])) left++;
    while (left < right && !/[a-z0-9]/i.test(s[right])) right--;
    if (s[left].toLowerCase() !== s[right].toLowerCase()) return false;
    left++; right--;
  }
  return true;
}
Practice questions for this pattern
7

String Fundamentals

Concept

Strings in most languages are immutable sequences of characters

Why It Works

Indexing is O(1) but concatenation in a loop is O(n²) — always use an array or StringBuilder to build strings. Key operations to know: split, join, indexOf, slice, charCodeAt, and converting between char and int. Off-by-one errors and empty-string edge cases are the most common bugs.

Pattern Example
Problem

Problem Reverse Words in a String. Split on whitespace, filter empty tokens, reverse the list, and join with a single space.

Implementation
function reverseWords(s) {
  return s.trim().split(/s+/).reverse().join(' ');
}
Practice questions for this pattern
8

String Hashing Basics

Concept

Hashing a string means converting it to a fixed-size representation that enables fast equality checks

Why It Works

In interviews this shows up as: using a frequency count array of size 26 as a hash key, rolling hash for substring matching, or comparing sorted characters as a canonical key. The key insight is that equal hashes imply equal strings only when the hash function is carefully designed.

Pattern Example
Problem

Problem Check if two strings are permutations. A count array of 26 characters is effectively a hash. Two strings are permutations if and only if their count arrays are identical.

Implementation
function isPermutation(a, b) {
  if (a.length !== b.length) return false;
  const count = new Array(26).fill(0);
  for (let i = 0; i < a.length; i++) {
    count[a.charCodeAt(i) - 97]++;
    count[b.charCodeAt(i) - 97]--;
  }
  return count.every(n => n === 0);
}
Practice questions for this pattern
9

Lexicographic Ordering

Concept

Lexicographic order is dictionary order: compare character by character from left to right and the first mismatch determines the order

Why It Works

This is used in sorting strings, finding the next permutation, checking if a string comes before another, and problems that ask for the smallest or largest valid string. Always remember: shorter strings that are a prefix of longer ones come first.

Pattern Example
Problem

Problem Largest Number. To arrange numbers so their concatenation is largest, use a custom comparator: compare string(a)+string(b) vs string(b)+string(a).

Implementation
function largestNumber(nums) {
  const strs = nums.map(String);
  strs.sort((a, b) => (b + a).localeCompare(a + b));
  return strs[0] === '0' ? '0' : strs.join('');
}
Practice questions for this pattern
DSA Practice Online - 150+ Coding Interview Questions | LeetCode Alternative | InstaMock - AI Mock Interview