String

44 problems · 49 approaches

Check whether a String is Palindrome or notEasy2 approaches

Problem

Return true if the string reads the same forwards and backwards.

Example 1

Input:  "level"
Output: true

Example 2

Input:  "hello"
Output: false

1. Two pointers

Time O(n)Space O(1)

Compare characters from both ends moving inward.

function isPalindrome(s) {
let l = 0, r = s.length - 1;
while (l < r) if (s[l++] !== s[r--]) return false;
return true;
}

2. Reverse and compare

Time O(n)Space O(n)

One-liner; O(n) space for the reversed copy. Fine to mention, not the answer they want.

const isPalindrome = (s) => s === [...s].reverse().join('');
Write a program to find the longest Palindrome in a stringMedium2 approaches

Problem

Given a string s, return its longest substring that is a palindrome. If several have the same length, return any one of them (commonly the first).

Example 1

Input:  "babad"
Output: "bab"

"aba" is also correct.

Example 2

Input:  "cbbd"
Output: "bb"

Also asked as: longest palindromic substring

1. Expand around center

Time O(n²)Space O(1)

Every palindrome has a center (a char, or a gap between two chars). Expand outward from all 2n−1 centers.

function longestPalindrome(s) {
let best = '';
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++) {
for (const p of [expand(i, i), expand(i, i + 1)])
if (p.length > best.length) best = p;
}
return best;
}

2. Dynamic programming

Time O(n²)Space O(n²)

dp[i][j] = s[i]===s[j] && dp[i+1][j-1]. Fill by substring length. Same time, O(n²) space, easier to reason about.

function longestPalindrome(s) {
const n = s.length;
const dp = Array.from({ length: n }, () => Array(n).fill(false));
let start = 0, maxLen = 1;
for (let i = 0; i < n; i++) dp[i][i] = true;
for (let len = 2; len <= n; len++) {
for (let i = 0; i + len - 1 < n; i++) {
const j = i + len - 1;
if (s[i] === s[j] && (len === 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
if (len > maxLen) { start = i; maxLen = len; }
}
}
}
return s.slice(start, start + maxLen);
}

Manacher’s algorithm solves this in O(n) but is rarely expected in interviews — mention it exists.

Longest Common PrefixEasy2 approaches

Problem

Given an array of strings, return the longest string that is a prefix of every one of them, or "" if there is none.

Example 1

Input:  ["flower", "flow", "flight"]
Output: "fl"

Example 2

Input:  ["dog", "racecar", "car"]
Output: ""

1. Vertical scan

Time O(N·M) chars totalSpace O(1)

Compare column 0 of every string, then column 1, … Stop at the first mismatch or shortest string end.

function longestCommonPrefix(strs) {
if (!strs.length) return '';
for (let i = 0; i < strs[0].length; i++) {
const c = strs[0][i];
for (const s of strs) if (i === s.length || s[i] !== c) return strs[0].slice(0, i);
}
return strs[0];
}

2. Sort, compare first and last

Time O(N log N · M)Space O(1)

After sorting, only the lexicographically smallest and largest strings matter.

function longestCommonPrefix(strs) {
if (!strs.length) return '';
strs.sort();
const a = strs[0], b = strs[strs.length - 1];
let i = 0;
while (i < a.length && a[i] === b[i]) i++;
return a.slice(0, i);
}
find the smallest window in a string containing all characters of another stringHard1 approach

Problem

Given strings s and t, return the shortest substring of s that contains every character of t, counting duplicates. Return "" if there is no such window.

Example 1

Input:  s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"

Example 2

Input:  s = "a", t = "aa"
Output: ""

t needs two a’s, but s has only one.

Also asked as: minimum window substring · smallest window that contains all characters of string itself

1. Sliding window with need-count

Time O(n)Space O(k)

Expand right until the window covers all needed chars, then contract left while it still does, tracking the smallest.

function minWindow(s, t) {
const need = new Map();
for (const c of t) need.set(c, (need.get(c) || 0) + 1);
let missing = t.length, start = 0, resStart = 0, resLen = Infinity;
for (let end = 0; end < s.length; end++) {
const c = s[end];
if (need.get(c) > 0) missing--;
need.set(c, (need.get(c) || 0) - 1);
while (missing === 0) {
if (end - start + 1 < resLen) { resLen = end - start + 1; resStart = start; }
const lc = s[start++];
need.set(lc, need.get(lc) + 1);
if (need.get(lc) > 0) missing++;
}
}
return resLen === Infinity ? '' : s.slice(resStart, resStart + resLen);
}
Balanced Parenthesis problemEasy1 approach

Problem

The string contains only the brackets ( ) { } [ ]. Return true if every opening bracket is closed by the same type of bracket, in the correct order.

Example 1

Input:  "{[()]}"
Output: true

Example 2

Input:  "([)]"
Output: false

The ( is closed by ] before its own ).

Also asked as: valid parentheses

1. Stack

Time O(n)Space O(n)

Push openers; on a closer, the stack top must be its matching opener.

function isValid(s) {
const pair = { ')': '(', ']': '[', '}': '{' };
const st = [];
for (const c of s) {
if (c === '(' || c === '[' || c === '{') st.push(c);
else if (st.pop() !== pair[c]) return false;
}
return st.length === 0;
}
Reverse a StringEasy1 approach

Problem

Reverse a string, or an array of characters in place with O(1) extra space.

Example 1

Input:  "hello"
Output: "olleh"

1. Two pointers (in place on a char array)

Time O(n)Space O(n) for the array

Swap ends inward. JS strings are immutable, so operate on an array.

function reverseString(s) {
const a = [...s];
let l = 0, r = a.length - 1;
while (l < r) { [a[l], a[r]] = [a[r], a[l]]; l++; r--; }
return a.join('');
}
Find duplicate characters in a stringEasy1 approach

Problem

Print every character that appears more than once in the string, together with how many times it appears.

Example 1

Input:  "programming"
Output: r: 2, g: 2, m: 2

Also asked as: Find Duplicate characters in a string

1. Frequency map

Time O(n)Space O(k)

Count each character; report those with count > 1.

function duplicates(s) {
const freq = new Map();
for (const c of s) freq.set(c, (freq.get(c) || 0) + 1);
return [...freq].filter(([, n]) => n > 1).map(([c, n]) => ({ char: c, count: n }));
}
Check whether one string is a rotation of anotherEasy1 approach

Problem

Return true if s2 can be obtained by rotating s1, that is, by moving some prefix of s1 to its end.

Example 1

Input:  s1 = "ABCD", s2 = "CDAB"
Output: true

Example 2

Input:  s1 = "ABCD", s2 = "ACBD"
Output: false

Also asked as: Write a Code to check whether one string is a rotation of another

1. Double-and-search

Time O(n) with a linear substring searchSpace O(n)

b is a rotation of a iff b is a substring of a + a (and lengths match).

function isRotation(a, b) {
return a.length === b.length && (a + a).includes(b);
}
Check whether a string is a valid shuffle of two stringsMedium1 approach

Problem

Given strings s1, s2 and result, return true if result interleaves s1 and s2: it uses all their characters, and the characters of each string keep their original order.

Example 1

Input:  s1 = "XY", s2 = "12", result = "1XY2"
Output: true

Example 2

Input:  s1 = "XY", s2 = "12", result = "Y1X2"
Output: false

Y comes before X, which breaks s1’s order.

Also asked as: Write a Program to check whether a string is a valid shuffle of two strings or not

1. Two-pointer merge check

Time O(n)Space O(1)

Walk the result; each character must match the front of a or b (preserving each source’s order).

function isValidShuffle(a, b, result) {
if (a.length + b.length !== result.length) return false;
let i = 0, j = 0;
for (const c of result) {
if (i < a.length && a[i] === c) i++;
else if (j < b.length && b[j] === c) j++;
else return false;
}
return i === a.length && j === b.length;
}

This greedy check can miss cases when a and b share a prefix; a fully correct solution uses interleaving DP: dp[i][j] = whether a[..i] + b[..j] forms result[..i+j].

Count and SayMedium1 approach

Problem

The count-and-say sequence starts with "1". Each next term reads the previous term aloud, group by group: "1" is read as "one 1" → "11", which is read as "two 1s" → "21", and so on. Given n, return the nth term.

Example 1

Input:  n = 4
Output: "1211"

1 → 11 → 21 → 1211 ("one 2, one 1").

Also asked as: Count and Say problem

1. Iterative run-length encoding

Time O(n · length)Space O(length)

Start from "1"; each next term describes the previous as "<count><digit>" runs.

function countAndSay(n) {
let s = '1';
for (let k = 1; k < n; k++) {
let next = '', i = 0;
while (i < s.length) {
let j = i;
while (j < s.length && s[j] === s[i]) j++;
next += (j - i) + s[i];
i = j;
}
s = next;
}
return s;
}
Longest Repeating SubsequenceMedium1 approach

Problem

Return the length of the longest subsequence that appears at least twice in the string, where the two occurrences never use the same index at the same position.

Example 1

Input:  "axxxy"
Output: 2

"xx" can be formed from indices (1,2) and again from (2,3).

Also asked as: Find Longest Recurring Subsequence in String · Longest Repeated Subsequence

1. LCS of the string with itself (excluding equal indices)

Time O(n²)Space O(n²)

Run the LCS DP on (s, s) but only count a match when the two indices differ.

function longestRepeatingSubseq(s) {
const n = s.length;
const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0));
for (let i = 1; i <= n; i++)
for (let j = 1; j <= n; j++)
dp[i][j] = s[i - 1] === s[j - 1] && i !== j
? dp[i - 1][j - 1] + 1
: Math.max(dp[i - 1][j], dp[i][j - 1]);
return dp[n][n];
}
Split a binary string into two substrings with equal 0s and 1sEasy1 approach

Problem

Split a binary string into the maximum number of consecutive pieces so that every piece has equal numbers of 0s and 1s. Return that count, or −1 if the whole string cannot be split this way.

Example 1

Input:  "0100110101"
Output: 4

"01" + "0011" + "01" + "01".

Also asked as: Split the Binary string into two substring with equal 0’s and 1’s

1. Running counts

Time O(n)Space O(1)

Scan; every time zeros equals ones so far, you can cut. Count such cut points.

function maxSplits(s) {
let zeros = 0, ones = 0, cuts = 0;
for (const c of s) {
c === '0' ? zeros++ : ones++;
if (zeros === ones) cuts++;
}
return zeros === ones ? cuts : -1; // -1 if the whole string can't be balanced
}
Word Wrap ProblemHard1 approach

Problem

Given word lengths and a line width k, split the words into lines in their original order. Words on a line are separated by one space, and a line may not exceed k characters. The cost of a line is (unused spaces at its end)², and the last line costs nothing. Return the minimum total cost.

Example 1

Input:  lengths = [3, 2, 2, 5], k = 6
Output: 10

Lines: [3] (3 spare → 9), [2, 2] (1 spare → 1), [5] (last line → 0).

Also asked as: Word Wrap Problem · Word Wrap Problem [VERY IMP]

1. DP over line breaks

Time O(n²)Space O(n)

dp[i] = min total cost to arrange words i..n. Try every valid end word for the current line; cost = (trailing spaces)² summed over lines (last line free).

function wordWrap(words, width) {
const n = words.length;
const dp = Array(n + 1).fill(Infinity);
dp[n] = 0;
for (let i = n - 1; i >= 0; i--) {
let lineLen = -1;
for (let j = i; j < n; j++) {
lineLen += words[j].length + 1;
if (lineLen > width) break;
const extra = j === n - 1 ? 0 : (width - lineLen) ** 2;
dp[i] = Math.min(dp[i], extra + dp[j + 1]);
}
}
return dp[0];
}
Edit DistanceMedium1 approach

Problem

Given strings a and b, return the minimum number of single-character operations (insert, delete or replace) needed to turn a into b.

Example 1

Input:  a = "horse", b = "ros"
Output: 3

Replace h→r, delete r, delete e.

Also asked as: EDIT Distance · Transform One String to Another using Minimum Number of Given Operation

1. 2-D DP (Levenshtein)

Time O(n · m)Space O(n · m)

dp[i][j] = edits to turn a[..i] into b[..j]: 0 if chars match on the diagonal, else 1 + min(insert, delete, replace).

function editDistance(a, b) {
const n = a.length, m = b.length;
const dp = Array.from({ length: n + 1 }, (_, i) => Array(m + 1).fill(0).map((_, j) => (i === 0 ? j : j === 0 ? i : 0)));
for (let i = 1; i <= n; i++)
for (let j = 1; j <= m; j++)
dp[i][j] = a[i - 1] === b[j - 1]
? dp[i - 1][j - 1]
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
return dp[n][m];
}
Next greater number with the same set of digitsMedium1 approach

Problem

Given a number as a string of digits, return the smallest number greater than it that uses exactly the same digits, or "not possible" if none exists.

Example 1

Input:  "218765"
Output: "251678"

Example 2

Input:  "4321"
Output: not possible

Also asked as: Find next greater number with same set of digits

1. Next permutation on the digit array

Time O(d)Space O(d)

Identical to "next permutation": find the rightmost ascending pair, swap with the next-larger digit to its right, reverse the suffix.

function nextGreater(numStr) {
const a = [...numStr];
let i = a.length - 2;
while (i >= 0 && a[i] >= a[i + 1]) i--;
if (i < 0) return 'no greater number';
let j = a.length - 1;
while (a[j] <= a[i]) j--;
[a[i], a[j]] = [a[j], a[i]];
return a.slice(0, i + 1).join('') + a.slice(i + 1).reverse().join('');
}
Word BreakMedium1 approach

Problem

Given a string s and a dictionary of words, return true if s can be split into a sequence of one or more dictionary words. Each word can be used any number of times.

Example 1

Input:  s = "leetcode", dict = ["leet", "code"]
Output: true

Example 2

Input:  s = "catsandog", dict = ["cats", "dog", "sand", "and", "cat"]
Output: false

Also asked as: Word break Problem · Word Break Problem using Backtracking

1. DP over prefixes

Time O(n² · L)Space O(n)

dp[i] = can s[0..i) be segmented. dp[i] is true if some j<i has dp[j] true and s[j..i) is in the dictionary.

function wordBreak(s, wordDict) {
const dict = new Set(wordDict);
const dp = Array(s.length + 1).fill(false);
dp[0] = true;
for (let i = 1; i <= s.length; i++) {
for (let j = i - 1; j >= 0; j--) {
if (dp[j] && dict.has(s.slice(j, i))) { dp[i] = true; break; }
}
}
return dp[s.length];
}
Convert a sentence into its mobile numeric keypad sequenceEasy1 approach

Problem

On an old phone keypad, a letter is typed by pressing its key repeatedly (a = 2, b = 22, c = 222, …, s = 7777, z = 9999), and a space is 0. Convert an uppercase sentence into its key sequence.

Example 1

Input:  "GEEKS"
Output: "4333355777"

Also asked as: Convert a Sentence into its equivalent mobile numeric keypad sequence

1. Lookup table

Time O(n)Space O(1)

Map each letter to its key presses (a→2, b→22, …); concatenate.

function keypadSequence(sentence) {
const keys = ['', '', 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'];
const map = {};
for (let d = 2; d <= 9; d++)
[...keys[d]].forEach((ch, i) => (map[ch] = String(d).repeat(i + 1)));
return [...sentence.toLowerCase()].map((c) => (c === ' ' ? '0' : map[c] || '')).join('');
}
Minimum bracket reversals to balance an expressionMedium1 approach

Problem

The expression contains only { and }. Reversing a bracket changes { to } or } to {. Return the minimum number of reversals needed to balance it, or −1 if that is impossible (an odd length).

Example 1

Input:  "}{{}}{{{"
Output: 3

Example 2

Input:  "{{{"
Output: -1

Also asked as: Minimum number of bracket reversals needed to make an expression balanced

1. Reduce, then count leftover open/close

Time O(n)Space O(1)

Cancel every matched "()". From the leftover "}}}...{{{" with c closers and o openers, the answer is ceil(c/2) + ceil(o/2).

function minReversals(s) {
if (s.length % 2) return -1;
let open = 0, close = 0;
for (const c of s) {
if (c === '{') open++;
else if (open > 0) open--; // matched a pair
else close++; // an unmatched '}'
}
return Math.ceil(open / 2) + Math.ceil(close / 2);
}
Minimum swaps for bracket balancingMedium1 approach

Problem

The string has n [ and n ] characters. In one swap you may exchange two adjacent characters. Return the minimum number of swaps needed to balance the string.

Example 1

Input:  "[]][]["
Output: 2

Example 2

Input:  "[[][]]"
Output: 0

Also asked as: Minimum number of swaps for bracket balancing

1. Track imbalance; each unmatched close costs its distance

Time O(n)Space O(1)

Scan; keep the running balance. On each unmatched "]", the number of swaps needed equals the count of "[" still waiting minus already-fixed — accumulate `imbalance` and add it when balance goes negative.

function minSwaps(s) {
let balance = 0, swaps = 0, imbalance = 0;
for (const c of s) {
if (c === '[') {
balance++;
if (imbalance > 0) { swaps += imbalance; imbalance--; }
} else {
balance--;
if (balance < 0) imbalance++;
}
}
return swaps;
}
Count all palindromic subsequencesHard1 approach

Problem

Return how many subsequences of the string are palindromes. Subsequences taken from different index sets count separately, even if they spell the same thing.

Example 1

Input:  "abcd"
Output: 4

Example 2

Input:  "aab"
Output: 4

"a", "a", "b" and "aa".

Also asked as: Count All Palindromic Subsequence in a given String

1. Interval DP

Time O(n²)Space O(n²)

dp[i][j] = count in s[i..j]. dp[i][j] = dp[i+1][j] + dp[i][j-1] − dp[i+1][j-1], and +dp[i+1][j-1]+1 when s[i]===s[j].

function countPalindromicSubseq(s) {
const n = s.length;
const dp = Array.from({ length: n }, () => Array(n).fill(0));
for (let i = 0; i < n; i++) dp[i][i] = 1;
for (let len = 2; len <= n; len++) {
for (let i = 0; i + len - 1 < n; i++) {
const j = i + len - 1;
dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - (i + 1 <= j - 1 ? dp[i + 1][j - 1] : 0);
if (s[i] === s[j]) dp[i][j] += 1 + (i + 1 <= j - 1 ? dp[i + 1][j - 1] : 0);
}
}
return dp[0][n - 1];
}
Search a word in a 2D grid of charactersMedium1 approach

Problem

Given a grid of characters and a word, find every cell from which the word can be read in a straight line in any of the 8 directions (horizontal, vertical or diagonal) without changing direction.

Example 1

Input:  grid = ["GEEKSFORGEEKS", "GEEKSQUIZGEEK", "IDEQAPRACTICE"], word = "GEEKS"
Output: [(0,0), (0,8), (1,0)]

Also asked as: Search a Word in a 2D Grid of characters · Count of number of given string in 2D character array

1. DFS from every cell in 8 directions

Time O(R·C·8·L)Space O(1)

From each cell that matches word[0], walk in a fixed direction matching subsequent characters.

function findWord(grid, word) {
const R = grid.length, C = grid[0].length;
const dirs = [[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];
const hits = [];
const ok = (r, c, dr, dc) => {
for (let k = 0; k < word.length; k++) {
const nr = r + dr * k, nc = c + dc * k;
if (nr < 0 || nc < 0 || nr >= R || nc >= C || grid[nr][nc] !== word[k]) return false;
}
return true;
};
for (let r = 0; r < R; r++)
for (let c = 0; c < C; c++)
if (grid[r][c] === word[0])
for (const [dr, dc] of dirs)
if (ok(r, c, dr, dc)) hits.push([r, c, dr, dc]);
return hits;
}
Boyer–Moore pattern searching (bad-character rule)Medium1 approach

Problem

Find every index where the pattern occurs in the text. Use the bad-character rule: on a mismatch, shift the pattern so that the mismatched text character lines up with its last occurrence in the pattern.

Example 1

Input:  text = "ABAAABCD", pattern = "ABC"
Output: [4]

Also asked as: Boyer Moore Algorithm for Pattern Searching

1. Bad-character heuristic

Time O(n/m) best, O(n·m) worstSpace O(alphabet)

Align the pattern; compare right-to-left. On a mismatch, shift so the mismatched text char lines up with its last occurrence in the pattern (or past it).

function boyerMoore(text, pat) {
const n = text.length, m = pat.length;
const last = new Map();
for (let i = 0; i < m; i++) last.set(pat[i], i);
const res = [];
let s = 0;
while (s <= n - m) {
let j = m - 1;
while (j >= 0 && pat[j] === text[s + j]) j--;
if (j < 0) { res.push(s); s += m; }
else {
const lo = last.has(text[s + j]) ? last.get(text[s + j]) : -1;
s += Math.max(1, j - lo);
}
}
return res;
}
Convert Roman numerals to decimalEasy1 approach

Problem

Convert a valid Roman numeral (I=1, V=5, X=10, L=50, C=100, D=500, M=1000) to an integer. A smaller symbol placed before a larger one is subtracted: IV = 4, CM = 900.

Example 1

Input:  "MCMXCIV"
Output: 1994

M (1000) + CM (900) + XC (90) + IV (4).

Also asked as: Converting Roman Numerals to Decimal

1. Subtract when a smaller value precedes a larger

Time O(n)Space O(1)

Add each symbol’s value, but if it is smaller than the next symbol, subtract it instead.

function romanToInt(s) {
const v = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 };
let total = 0;
for (let i = 0; i < s.length; i++) {
if (i + 1 < s.length && v[s[i]] < v[s[i + 1]]) total -= v[s[i]];
else total += v[s[i]];
}
return total;
}
Minimum flips to make a binary string alternateEasy1 approach

Problem

Return the minimum number of characters to flip so that no two adjacent characters of the binary string are equal.

Example 1

Input:  "0001010111"
Output: 2

Flip to "0101010101".

Also asked as: Number of flips to make binary string alternate

1. Compare against both alternating patterns

Time O(n)Space O(1)

Count mismatches versus "0101…"; mismatches versus "1010…" is n minus that. Answer is the smaller.

function minFlips(s) {
let diff = 0;
for (let i = 0; i < s.length; i++) {
const expected = i % 2 === 0 ? '0' : '1';
if (s[i] !== expected) diff++;
}
return Math.min(diff, s.length - diff);
}
Find the first repeated word in a stringEasy1 approach

Problem

Given a sentence, return the first word that appears again later in the sentence (the earliest second occurrence), or report that no word repeats.

Example 1

Input:  "he had had quite enough of this nonsense"
Output: "had"

Also asked as: Find the first repeated word in string

1. Set of seen words

Time O(n)Space O(w)

Split on whitespace; the first word already in the set is the answer.

function firstRepeatedWord(s) {
const seen = new Set();
for (const w of s.toLowerCase().split(/\s+/)) {
if (seen.has(w)) return w;
seen.add(w);
}
return null;
}
Smallest window containing all distinct characters of itselfMedium1 approach

Problem

Return the length of the smallest substring that contains every distinct character of the whole string.

Example 1

Input:  "aabcbcdbca"
Output: 4

"dbca" contains a, b, c and d.

Also asked as: Write a program tofind the smallest window that contains all characters of string itself

1. Sliding window over the distinct-count target

Time O(n)Space O(k)

Target = number of distinct characters in the whole string. Expand right until the window has them all, then contract left.

function smallestDistinctWindow(s) {
const target = new Set(s).size;
const count = new Map();
let have = 0, start = 0, best = s;
for (let end = 0; end < s.length; end++) {
const c = s[end];
count.set(c, (count.get(c) || 0) + 1);
if (count.get(c) === 1) have++;
while (have === target) {
if (end - start + 1 < best.length) best = s.slice(start, end + 1);
const lc = s[start++];
count.set(lc, count.get(lc) - 1);
if (count.get(lc) === 0) have--;
}
}
return best;
}
Rearrange a string so no two adjacent characters are the sameMedium1 approach

Problem

Rearrange the characters of the string so that no two adjacent characters are equal. Return any valid arrangement, or "" if none exists.

Example 1

Input:  "aab"
Output: "aba"

Example 2

Input:  "aaab"
Output: ""

Three a’s cannot be separated by a single b.

Also asked as: Rearrange characters in a string such that no two adjacent are same · Leetcode- reorganize strings · reorganize strings

1. Greedy — always place the most frequent remaining char

Time O(n · k)Space O(k)

Repeatedly append the highest-count character that is not the one just placed. Impossible iff some char’s count exceeds ceil(n/2).

function reorganize(s) {
const freq = new Map();
for (const c of s) freq.set(c, (freq.get(c) || 0) + 1);
let res = '', prev = '';
for (let i = 0; i < s.length; i++) {
let best = '';
for (const [c, n] of freq) if (n > 0 && c !== prev && (best === '' || n > freq.get(best))) best = c;
if (best === '') return '';
res += best;
freq.set(best, freq.get(best) - 1);
prev = best;
}
return res;
}

With a real max-heap keyed by frequency this is O(n log k).

Minimum characters to add at front to make a string palindromeMedium1 approach

Problem

Return the minimum number of characters that must be added to the front of the string to make it a palindrome.

Example 1

Input:  "AACECAAAA"
Output: 2

Add "AA" to get "AAAACECAAAA".

Example 2

Input:  "ABC"
Output: 2

Add "CB" to get "CBABC".

Also asked as: Minimum characters to be added at front to make string palindrome

1. KMP failure function on s + "#" + reverse(s)

Time O(n)Space O(n)

The longest prefix of s that is also a suffix of reverse(s) is the longest palindromic prefix. Characters to add = n − that length.

function minCharsFront(s) {
const combined = s + '#' + [...s].reverse().join('');
const lps = Array(combined.length).fill(0);
for (let i = 1, len = 0; i < combined.length; ) {
if (combined[i] === combined[len]) lps[i++] = ++len;
else if (len) len = lps[len - 1];
else lps[i++] = 0;
}
return s.length - lps[lps.length - 1];
}
Group all anagrams togetherMedium1 approach

Problem

Given a list of words, group together the words that are anagrams of each other (same letters, rearranged). Return the groups in any order.

Example 1

Input:  ["eat", "tea", "tan", "ate", "nat", "bat"]
Output: [["eat","tea","ate"], ["tan","nat"], ["bat"]]

Also asked as: Given a sequence of words, print all anagrams together

1. Bucket by sorted characters

Time O(n · k log k)Space O(n · k)

Two words are anagrams iff their sorted-letter strings match; use that as a map key.

function groupAnagrams(words) {
const groups = new Map();
for (const w of words) {
const key = [...w].sort().join('');
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(w);
}
return [...groups.values()];
}

Use a 26-length count vector as the key to drop the sort → O(n·k).

Generate all valid IP addresses from a stringMedium1 approach

Problem

Given a string of digits, return every valid IPv4 address you can form by inserting three dots. Each of the four parts must be between 0 and 255 and must not have a leading zero (so "0" is allowed but "01" is not).

Example 1

Input:  "25525511135"
Output: ["255.255.11.135", "255.255.111.35"]

Also asked as: Program to generate all possible valid IP addresses from given string

1. Backtracking with 4 segments

Time O(1) — at most 3⁴ splitsSpace O(1)

Place 3 dots. Each of the 4 parts is 1–3 digits, ≤ 255, and has no leading zero (unless it is "0").

function restoreIps(s) {
const res = [];
const valid = (seg) => seg.length >= 1 && seg.length <= 3 &&
(seg === '0' || (seg[0] !== '0' && +seg <= 255));
const bt = (start, parts) => {
if (parts.length === 4) { if (start === s.length) res.push(parts.join('.')); return; }
for (let len = 1; len <= 3 && start + len <= s.length; len++) {
const seg = s.slice(start, start + len);
if (valid(seg)) bt(start + len, [...parts, seg]);
}
};
bt(0, []);
return res;
}
Recursively remove all adjacent duplicatesMedium1 approach

Problem

Remove every run of two or more equal adjacent characters. Repeat on the result until no adjacent duplicates remain, then return what is left.

Example 1

Input:  "azxxzy"
Output: "ay"

Removing "xx" gives "azzy". Removing "zz" gives "ay".

Also asked as: Recursively remove all adjacent duplicates

1. Stack

Time O(n)Space O(n)

Push chars; if the incoming char equals the stack top, pop the whole run instead of pushing (and skip the rest of that run).

function removeAdjacentDuplicates(s) {
const st = [];
let i = 0;
while (i < s.length) {
if (st.length && st[st.length - 1] === s[i]) {
const dup = s[i];
while (i < s.length && s[i] === dup) i++;
st.pop();
} else {
st.push(s[i++]);
}
}
return st.join('');
}
Wildcard string matching (? and *)Hard1 approach

Problem

Return true if the pattern matches the whole string. In the pattern, "?" matches any single character and "*" matches any sequence of characters, including none.

Example 1

Input:  s = "adceb", p = "*a*b"
Output: true

Example 2

Input:  s = "acdcb", p = "a*c?b"
Output: false

Also asked as: String matching where one string contains wildcard characters

1. 2-D DP

Time O(n · m)Space O(n · m)

dp[i][j] = does pattern[..j] match text[..i]. "?" matches any single char; "*" matches empty (dp[i][j-1]) or one-more (dp[i-1][j]).

function isMatch(text, pattern) {
const n = text.length, m = pattern.length;
const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(false));
dp[0][0] = true;
for (let j = 1; j <= m; j++) if (pattern[j - 1] === '*') dp[0][j] = dp[0][j - 1];
for (let i = 1; i <= n; i++)
for (let j = 1; j <= m; j++) {
if (pattern[j - 1] === '*') dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
else if (pattern[j - 1] === '?' || pattern[j - 1] === text[i - 1]) dp[i][j] = dp[i - 1][j - 1];
}
return dp[n][m];
}
Number of customers who could not get a computerMedium1 approach

Problem

A café has n computers. The string lists events: the first time a letter appears, that customer arrives; the second time, they leave. An arriving customer takes a free computer, or leaves without one if all are busy (and their later departure changes nothing). Return how many customers could not get a computer.

Example 1

Input:  n = 2, "ABBAJJKZKZ"
Output: 0

Example 2

Input:  n = 1, "GACCBDDBAGEE"
Output: 1

A arrives while G is still using the only computer.

Also asked as: Function to find Number of customers who could not get a computer

1. Simulate seats with a set

Time O(n)Space O(seats)

First time a customer id appears they take a seat (if one is free, else they walk away). Second appearance frees the seat.

function turnedAway(seq, k) {
const seated = new Set();
const walkedAway = new Set();
let turned = 0;
for (const id of seq) {
if (seated.has(id)) seated.delete(id); // leaving
else if (walkedAway.has(id)) walkedAway.delete(id); // second visit after being turned away
else if (seated.size < k) seated.add(id); // gets a seat
else { turned++; walkedAway.add(id); } // no seat
}
return turned;
}
Check if two strings are isomorphicEasy1 approach

Problem

Two strings are isomorphic if the characters of s can be replaced to get t, using a consistent one-to-one mapping (no two characters map to the same character). Return true if they are.

Example 1

Input:  s = "egg", t = "add"
Output: true

Example 2

Input:  s = "foo", t = "bar"
Output: false

Example 3

Input:  s = "badc", t = "baba"
Output: false

Also asked as: Check if two given strings are isomorphic to each other

1. Two consistency maps

Time O(n)Space O(1)

Each char of a must map to exactly one char of b and vice-versa.

function isIsomorphic(a, b) {
if (a.length !== b.length) return false;
const ab = new Map(), ba = new Map();
for (let i = 0; i < a.length; i++) {
if (ab.has(a[i]) && ab.get(a[i]) !== b[i]) return false;
if (ba.has(b[i]) && ba.get(b[i]) !== a[i]) return false;
ab.set(a[i], b[i]);
ba.set(b[i], a[i]);
}
return true;
}
Valid AnagramEasy2 approaches

Problem

Return true if t is an anagram of s — the same characters with the same counts, possibly in a different order.

Example 1

Input:  s = "anagram", t = "nagaram"
Output: true

Example 2

Input:  s = "rat", t = "car"
Output: false

1. Sort both

Time O(n log n)Space O(n)

Anagrams have identical sorted forms.

const isAnagram = (s, t) =>
s.length === t.length && [...s].sort().join('') === [...t].sort().join('');

2. Count array

Time O(n)Space O(1) — 26 letters (use a Map for Unicode)

Increment for s, decrement for t; every count must end at zero.

function isAnagram(s, t) {
if (s.length !== t.length) return false;
const cnt = new Array(26).fill(0);
for (let i = 0; i < s.length; i++) {
cnt[s.charCodeAt(i) - 97]++;
cnt[t.charCodeAt(i) - 97]--;
}
return cnt.every((c) => c === 0);
}
First Unique Character in a StringEasy1 approach

Problem

Return the index of the first character that appears exactly once in the string, or −1 if there is none.

Example 1

Input:  "leetcode"
Output: 0

Example 2

Input:  "loveleetcode"
Output: 2

Example 3

Input:  "aabb"
Output: -1

1. Two passes with a count

Time O(n)Space O(1) — alphabet size

Count every character, then return the first index whose count is 1.

function firstUniqChar(s) {
const cnt = new Map();
for (const c of s) cnt.set(c, (cnt.get(c) ?? 0) + 1);
for (let i = 0; i < s.length; i++) if (cnt.get(s[i]) === 1) return i;
return -1;
}
Encode and Decode StringsMedium1 approach

Problem

Design encode(list of strings) → one string, and decode(that string) → the original list. The strings may contain any character, including whatever you might choose as a delimiter.

Example 1

Input:  ["neet", "co#de", ""]
Output: decode(encode(input)) returns ["neet", "co#de", ""]

1. Length prefix

Time O(total length)Space O(total length)

Write each string as "<length>#<string>". The decoder reads digits up to "#", then exactly that many characters — so "#" inside a string is harmless.

const encode = (strs) => strs.map((s) => s.length + '#' + s).join('');
function decode(str) {
const res = [];
let i = 0;
while (i < str.length) {
const hash = str.indexOf('#', i);
const len = Number(str.slice(i, hash));
res.push(str.slice(hash + 1, hash + 1 + len));
i = hash + 1 + len;
}
return res;
}

Joining with a delimiter breaks when a string contains the delimiter — say why you rejected it.

Valid PalindromeEasy1 approach

Problem

Return true if the string reads the same forwards and backwards after lowercasing it and removing every character that is not a letter or digit.

Example 1

Input:  "A man, a plan, a canal: Panama"
Output: true

Example 2

Input:  "race a car"
Output: false

1. Two pointers, skip non-alphanumerics

Time O(n)Space O(1)

Walk inward from both ends, skipping anything that is not a letter or digit, comparing case-insensitively.

function isPalindrome(s) {
const ok = (c) => /[a-z0-9]/i.test(c);
let l = 0, r = s.length - 1;
while (l < r) {
if (!ok(s[l])) l++;
else if (!ok(s[r])) r--;
else if (s[l].toLowerCase() !== s[r].toLowerCase()) return false;
else { l++; r--; }
}
return true;
}
Longest Substring Without Repeating CharactersMedium2 approaches

Problem

Return the length of the longest substring (contiguous) that contains no repeated character.

Example 1

Input:  "abcabcbb"
Output: 3

"abc".

Example 2

Input:  "pwwkew"
Output: 3

"wke" — "pwke" is a subsequence, not a substring.

1. Window with a set

Time O(n)Space O(alphabet)

Grow right; while the new char is already in the window, remove from the left.

function lengthOfLongestSubstring(s) {
const win = new Set();
let l = 0, best = 0;
for (let r = 0; r < s.length; r++) {
while (win.has(s[r])) win.delete(s[l++]);
win.add(s[r]);
best = Math.max(best, r - l + 1);
}
return best;
}

2. Jump using last-seen index

Time O(n)Space O(alphabet)

Store each char’s last index; on a repeat inside the window, jump l straight past it.

function lengthOfLongestSubstring(s) {
const last = new Map();
let l = 0, best = 0;
for (let r = 0; r < s.length; r++) {
if (last.has(s[r]) && last.get(s[r]) >= l) l = last.get(s[r]) + 1;
last.set(s[r], r);
best = Math.max(best, r - l + 1);
}
return best;
}
Longest Repeating Character ReplacementMedium1 approach

Problem

The string contains uppercase letters. You may replace at most k characters with any letter. Return the length of the longest substring you can make that consists of a single repeated letter.

Example 1

Input:  s = "ABAB", k = 2
Output: 4

Example 2

Input:  s = "AABABBA", k = 1
Output: 4

Replace the A at index 3: "AABBBBA" contains "BBBB".

1. Window with max frequency

Time O(n)Space O(26)

A window is valid when length − (count of its most common char) ≤ k. maxFreq never needs to decrease: a smaller one cannot produce a longer answer.

function characterReplacement(s, k) {
const cnt = new Array(26).fill(0);
let l = 0, maxFreq = 0, best = 0;
for (let r = 0; r < s.length; r++) {
maxFreq = Math.max(maxFreq, ++cnt[s.charCodeAt(r) - 65]);
while (r - l + 1 - maxFreq > k) cnt[s.charCodeAt(l++) - 65]--;
best = Math.max(best, r - l + 1);
}
return best;
}
Permutation in StringMedium1 approach

Problem

Return true if s2 contains some permutation of s1 as a substring — a window of s2 with exactly the same character counts as s1.

Example 1

Input:  s1 = "ab", s2 = "eidbaooo"
Output: true

"ba".

Example 2

Input:  s1 = "ab", s2 = "eidboaoo"
Output: false

1. Fixed window of counts

Time O(|s2|)Space O(26)

Slide a window of length |s1| over s2 and compare the 26 counts. Track how many letters match to make each step O(1).

function checkInclusion(s1, s2) {
if (s1.length > s2.length) return false;
const need = new Array(26).fill(0), win = new Array(26).fill(0);
const idx = (c) => c.charCodeAt(0) - 97;
for (let i = 0; i < s1.length; i++) { need[idx(s1[i])]++; win[idx(s2[i])]++; }
let matches = 0;
for (let i = 0; i < 26; i++) if (need[i] === win[i]) matches++;
for (let r = s1.length; r < s2.length; r++) {
if (matches === 26) return true;
for (const [c, d] of [[idx(s2[r]), 1], [idx(s2[r - s1.length]), -1]]) {
if (win[c] === need[c]) matches--;
win[c] += d;
if (win[c] === need[c]) matches++;
}
}
return matches === 26;
}

Comparing the two 26-arrays on every step is also O(26·n) and is fine to present first.