Skip to content
DSA in JavaScript
Foundations

Strings

Fundamentals ยท Starter

Complexity

OperationTimeNote
s[i], s.at(i), s.charAt(i)O(1)
s.lengthO(1)
s + t, s.concat(t)O(n)Creates a new string
s.slice(a, b), s.substring(a, b)O(n)Copy of the range
s.indexOf(t), s.includes(t)O(n ร— m) worstn text, m pattern
s.split(''), [...s]O(n)
a === bO(n)Compares characters

A string is a sequence of characters. In JavaScript it is immutable: you never change a string, you build a new one. That single fact explains most performance questions about strings.

Building strings

Appending in a loop with += is fine for small inputs, but for large ones collect pieces in an array and join once.

const parts = [];
for (const word of words) parts.push(word.toUpperCase());
const result = parts.join(' ');

Characters as numbers

'a'.charCodeAt(0) is 97. Subtracting it maps letters to 0 to 25, which lets you count letters in a fixed array of 26 instead of a Map.

const counts = Array(26).fill(0);
for (const ch of word) counts[ch.charCodeAt(0) - 97]++;

JavaScript specifics

  • Strings are UTF-16. '๐Ÿ˜€'.length is 2. Use [...s] or for...of to walk by code point.
  • slice accepts negative indexes, substring does not.
  • localeCompare for locale-aware ordering. The default < compares code units.
  • s.split(' ') on text with repeated spaces produces empty strings.
  • Strings compare by value ('a' === 'a'), unlike objects.

Techniques

  • Two pointers from both ends: palindromes, reversing.
  • Sliding window: longest substring with a property.
  • Frequency count: anagrams, permutations in a string.
  • Hash map or set: duplicates, first unique character.
  • Stack: matching brackets, decoding nested patterns.
  • String matching (KMP, Rabin-Karp, Z) for pattern search, in the advanced section.

Common mistakes

  • Comparing with == or forgetting strings are immutable (s[0] = 'x' silently does nothing).
  • Using += in a hot loop over a huge input.
  • Forgetting case or whitespace rules in the problem statement.

Code

strings.js
export const isPalindrome = s => {
  const t = s.toLowerCase().replace(/[^a-z0-9]/g, '');
  for (let l = 0, r = t.length - 1; l < r; l++, r--) if (t[l] !== t[r]) return false;
  return true;
}; // O(n) time

export const isAnagram = (a, b) => {
  if (a.length !== b.length) return false;
  const counts = Array(26).fill(0);
  for (let i = 0; i < a.length; i++) {
    counts[a.charCodeAt(i) - 97]++;
    counts[b.charCodeAt(i) - 97]--;
  }
  return counts.every(c => c === 0);
}; // O(n) time, O(1) space

export const reverseWords = s => s.trim().split(/\s+/).reverse().join(' ');

export const firstUniqueChar = s => {
  const freq = new Map();
  for (const ch of s) freq.set(ch, (freq.get(ch) ?? 0) + 1);
  return [...s].findIndex(ch => freq.get(ch) === 1);
};

export const compress = s => { // "aaabb" -> "a3b2"
  const out = [];
  for (let i = 0, j = 0; i < s.length; i = j) {
    while (j < s.length && s[j] === s[i]) j++;
    out.push(s[i], j - i);
  }
  return out.join('');
};

Practice

Spotted a mistake or something unclear? Report it on GitHub.

On this page