π― Top 25 PHP String Interview Questions
Parent: Strings PHP Index
Quick Reference
| # | Problem | Difficulty | Pattern | Time |
|---|---|---|---|---|
| 1 | Reverse String | E | Two Pointers | O(n) |
| 2 | Valid Palindrome | E | Two Pointers | O(n) |
| 3 | Valid Anagram | E | Frequency Map | O(n) |
| 4 | First Unique Character | E | Frequency Map | O(n) |
| 5 | Reverse Words | E | Split + Reverse | O(n) |
| 6 | String Compression | M | Two Pointers | O(n) |
| 7 | Valid Parentheses | E | Stack | O(n) |
| 8 | Longest Substring No Repeat | M | Sliding Window | O(n) |
| 9 | Longest Palindromic Substring | M | Expand Center | O(nΒ²) |
| 10 | Group Anagrams | M | Hash Map | O(nΒ·k log k) |
| 11 | Implement strStr (KMP) | E | KMP | O(n+m) |
| 12 | Count & Say | M | Simulation | O(nΒ·2βΏ) |
| 13 | Minimum Window Substring | H | Sliding Window | O(n+m) |
| 14 | Encode/Decode Strings | M | Delimiter | O(n) |
| 15 | Longest Common Prefix | E | Horizontal Scan | O(nΒ·m) |
| 16 | Roman to Integer | E | Hash Map | O(n) |
| 17 | Integer to Roman | M | Greedy | O(1) |
| 18 | Decode String | M | Stack | O(n) |
| 19 | Word Break | M | Recursion + Memo | O(nΒ²) |
| 20 | Palindrome Partitioning | M | Backtracking | O(nΒ·2βΏ) |
| 21 | Scramble String | H | Recursion + Memo | O(nβ΄) |
| 22 | Zigzag Conversion | M | Simulation | O(n) |
| 23 | Wildcard Matching | H | DP | O(mΓn) |
| 24 | Longest Repeating Char Replace | M | Sliding Window | O(n) |
| 25 | Minimum Deletions to Make Freq Unique | M | Greedy | O(n) |
Q1. Reverse String [E][LC-344]
π§ How to Approach This
Step 1: Two pointers. Swap first and last, move inward.
function reverseString(array &$s): void {
$lo = 0; $hi = count($s) - 1;
while ($lo < $hi) {
[$s[$lo], $s[$hi]] = [$s[$hi], $s[$lo]];
$lo++; $hi--;
}
}
$chars = ['h','e','l','l','o'];
reverseString($chars);
echo implode('', $chars); // "olleh"
Time: O(n) | Space: O(1)
Q2. Valid Palindrome [E][LC-125]
π§ How to Approach This
Step 1: Skip non-alphanumeric. Compare ends moving inward. Case-insensitive.
function isPalindrome(string $s): bool {
$lo = 0; $hi = strlen($s) - 1;
while ($lo < $hi) {
while ($lo < $hi && !ctype_alnum($s[$lo])) $lo++;
while ($lo < $hi && !ctype_alnum($s[$hi])) $hi--;
if (strtolower($s[$lo]) !== strtolower($s[$hi])) return false;
$lo++; $hi--;
}
return true;
}
echo isPalindrome("A man, a plan, a canal: Panama") ? 'yes' : 'no'; // yes
Time: O(n) | Space: O(1)
Q3. Valid Anagram [E][LC-242]
π§ How to Approach This
Step 1: Same length? Count frequencies. Increment for s, decrement for t. Any nonzero β false.
function isAnagram(string $s, string $t): bool {
if (strlen($s) !== strlen($t)) return false;
$freq = array_fill(0, 26, 0);
for ($i = 0; $i < strlen($s); $i++) {
$freq[ord($s[$i]) - ord('a')]++;
$freq[ord($t[$i]) - ord('a')]--;
}
foreach ($freq as $v) if ($v !== 0) return false;
return true;
}
echo isAnagram("anagram","nagaram") ? 'yes' : 'no'; // yes
echo isAnagram("rat","car") ? 'yes' : 'no'; // no
Time: O(n) | Space: O(1)
Q4. First Unique Character [E][LC-387]
π§ How to Approach This
Step 1: Count all frequencies. Find first char with count == 1.
function firstUniqChar(string $s): int {
$freq = array_fill(0, 26, 0);
for ($i = 0; $i < strlen($s); $i++)
$freq[ord($s[$i]) - ord('a')]++;
for ($i = 0; $i < strlen($s); $i++)
if ($freq[ord($s[$i]) - ord('a')] === 1) return $i;
return -1;
}
echo firstUniqChar("leetcode"); // 0
echo firstUniqChar("loveleetcode"); // 2
Time: O(n) | Space: O(1)
Q5. Reverse Words in String [M][LC-151]
π§ How to Approach This
Step 1: Trim, split by whitespace (handles multiple spaces), reverse, rejoin.
function reverseWords(string $s): string {
$words = preg_split('/\s+/', trim($s));
return implode(' ', array_reverse($words));
}
echo reverseWords(" the sky is blue "); // "blue is sky the"
echo reverseWords("a good example"); // "example good a"
Time: O(n) | Space: O(n)
Q6. String Compression [M][LC-443]
π§ How to Approach This
Step 1: Two pointers β write pointer
wand read pointeri.Count consecutive chars. Write char + count (if > 1).
function compress(array &$chars): int {
$w = 0; $i = 0; $n = count($chars);
while ($i < $n) {
$c = $chars[$i]; $count = 0;
while ($i < $n && $chars[$i] === $c) { $i++; $count++; }
$chars[$w++] = $c;
if ($count > 1) foreach (str_split((string)$count) as $d) $chars[$w++] = $d;
}
return $w;
}
$c = ['a','a','b','b','c','c','c'];
echo compress($c); // 6 β chars becomes ['a','2','b','2','c','3']
Time: O(n) | Space: O(1)
Q7. Valid Parentheses [E][LC-20]
π§ How to Approach This
Step 1: Stack. Push open brackets. On close: if stack empty or top doesn't match β invalid.
function isValid(string $s): bool {
$stack = [];
$map = [')'=>'(', ']'=>'[', '}'=>'{'];
foreach (str_split($s) as $c) {
if (in_array($c, ['(','[','{'])) { $stack[] = $c; }
elseif (isset($map[$c])) {
if (empty($stack) || array_pop($stack) !== $map[$c]) return false;
}
}
return empty($stack);
}
echo isValid("()[]{}") ? 'yes' : 'no'; // yes
echo isValid("([)]") ? 'yes' : 'no'; // no
Time: O(n) | Space: O(n)
Q8. Longest Substring Without Repeating Characters [M][LC-3]
π§ How to Approach This
Step 1 β Sliding Window: Map char β last index. Move left past duplicate. Track max.
function lengthOfLongestSubstring(string $s): int {
$map = []; $max = 0; $left = 0;
for ($r = 0; $r < strlen($s); $r++) {
$c = $s[$r];
if (isset($map[$c]) && $map[$c] >= $left) $left = $map[$c] + 1;
$map[$c] = $r;
$max = max($max, $r - $left + 1);
}
return $max;
}
echo lengthOfLongestSubstring("abcabcbb"); // 3
echo lengthOfLongestSubstring("pwwkew"); // 3
Time: O(n) | Space: O(k) where k = charset size
Q9. Longest Palindromic Substring [M][LC-5]
π§ How to Approach This
Step 1 β Expand Around Center: For each index, expand outward (odd + even centers). Track longest.
function longestPalindrome(string $s): string {
$best = '';
$expand = function(int $lo, int $hi) use ($s): string {
while ($lo >= 0 && $hi < strlen($s) && $s[$lo] === $s[$hi]) { $lo--; $hi++; }
return substr($s, $lo + 1, $hi - $lo - 1);
};
for ($i = 0; $i < strlen($s); $i++) {
foreach ([$expand($i,$i), $expand($i,$i+1)] as $p)
if (strlen($p) > strlen($best)) $best = $p;
}
return $best;
}
echo longestPalindrome("babad"); // "bab"
echo longestPalindrome("cbbd"); // "bb"
Time: O(nΒ²) | Space: O(1)
Q10. Group Anagrams [M][LC-49]
π§ How to Approach This
Step 1 β Key insight: Anagrams share the same sorted characters. Use sorted form as hash key.
function groupAnagrams(array $strs): array {
$map = [];
foreach ($strs as $s) {
$chars = str_split($s); sort($chars);
$map[implode('', $chars)][] = $s;
}
return array_values($map);
}
$res = groupAnagrams(["eat","tea","tan","ate","nat","bat"]);
// [["eat","tea","ate"],["tan","nat"],["bat"]]
Time: O(n Γ k log k) | Space: O(nΓk)
Q11. Implement strStr / Needle in Haystack [E][LC-28]
π§ How to Approach This
Step 1 β KMP: Build LPS array. O(n+m) search. Note: PHP's
strposis built-in, but know KMP for interviews.
function strStr(string $haystack, string $needle): int {
if ($needle === '') return 0;
$n = strlen($haystack); $m = strlen($needle);
// Build LPS
$lps = array_fill(0, $m, 0);
for ($len = 0, $i = 1; $i < $m; ) {
if ($needle[$i] === $needle[$len]) { $lps[$i++] = ++$len; }
elseif ($len > 0) { $len = $lps[$len - 1]; }
else { $lps[$i++] = 0; }
}
// KMP search
for ($i = 0, $j = 0; $i < $n; ) {
if ($haystack[$i] === $needle[$j]) { $i++; $j++; }
if ($j === $m) return $i - $j;
elseif ($i < $n && $haystack[$i] !== $needle[$j])
$j > 0 ? $j = $lps[$j - 1] : $i++;
}
return -1;
}
echo strStr("hello", "ll"); // 2
echo strStr("aaaaa", "bba"); // -1
Time: O(n+m) | Space: O(m)
Q12. Count and Say [M][LC-38]
π§ How to Approach This
Step 1 β Simulate: Count consecutive digits. Say "n times digit".
function countAndSay(int $n): string {
$s = '1';
for ($i = 1; $i < $n; $i++) {
$next = ''; $j = 0;
while ($j < strlen($s)) {
$c = $s[$j]; $cnt = 0;
while ($j < strlen($s) && $s[$j] === $c) { $j++; $cnt++; }
$next .= $cnt . $c;
}
$s = $next;
}
return $s;
}
echo countAndSay(1); // "1"
echo countAndSay(4); // "1211"
echo countAndSay(5); // "111221"
Time: O(n Γ 2βΏ) | Space: O(2βΏ)
Q13. Minimum Window Substring [H][LC-76]
π§ How to Approach This
Step 1 β Sliding Window: Track
needmap andformedcount. Expand right, shrink left when all found.
function minWindow(string $s, string $t): string {
$need = []; foreach (str_split($t) as $c) $need[$c] = ($need[$c] ?? 0) + 1;
$have = []; $formed = 0; $required = count($need);
$best = ''; $lo = 0;
for ($r = 0; $r < strlen($s); $r++) {
$c = $s[$r]; $have[$c] = ($have[$c] ?? 0) + 1;
if (isset($need[$c]) && $have[$c] === $need[$c]) $formed++;
while ($formed === $required) {
$win = substr($s, $lo, $r - $lo + 1);
if ($best === '' || strlen($win) < strlen($best)) $best = $win;
$lc = $s[$lo++]; $have[$lc]--;
if (isset($need[$lc]) && $have[$lc] < $need[$lc]) $formed--;
}
}
return $best;
}
echo minWindow("ADOBECODEBANC", "ABC"); // "BANC"
Time: O(n+m) | Space: O(n+m)
Q14. Longest Common Prefix [E][LC-14]
π§ How to Approach This
Step 1 β Horizontal Scan: Take first string as prefix. Shorten it until all strings start with it.
function longestCommonPrefix(array $strs): string {
if (empty($strs)) return '';
$prefix = $strs[0];
foreach ($strs as $s) {
while (strpos($s, $prefix) !== 0)
$prefix = substr($prefix, 0, strlen($prefix) - 1);
if ($prefix === '') return '';
}
return $prefix;
}
echo longestCommonPrefix(["flower","flow","flight"]); // "fl"
echo longestCommonPrefix(["dog","racecar","car"]); // ""
Time: O(nΓm) | Space: O(1)
Q15. Roman to Integer [E][LC-13]
π§ How to Approach This
Step 1: Map symbols to values. If current < next β subtract. Else add.
function romanToInt(string $s): int {
$map = ['I'=>1,'V'=>5,'X'=>10,'L'=>50,'C'=>100,'D'=>500,'M'=>1000];
$res = 0;
for ($i = 0; $i < strlen($s); $i++) {
$curr = $map[$s[$i]];
$next = isset($s[$i+1]) ? $map[$s[$i+1]] : 0;
$res += ($curr < $next) ? -$curr : $curr;
}
return $res;
}
echo romanToInt("III"); // 3
echo romanToInt("MCMXCIV"); // 1994
Time: O(n) | Space: O(1)
Q16. Integer to Roman [M][LC-12]
π§ How to Approach This
Step 1 β Greedy: Use list of values+symbols from largest to smallest. Subtract greedily.
function intToRoman(int $num): string {
$vals = [1000,900,500,400,100,90,50,40,10,9,5,4,1];
$syms = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"];
$res = '';
foreach ($vals as $i => $v) {
while ($num >= $v) { $res .= $syms[$i]; $num -= $v; }
}
return $res;
}
echo intToRoman(1994); // "MCMXCIV"
echo intToRoman(58); // "LVIII"
Time: O(1) bounded | Space: O(1)
Q17. Decode String [M][LC-394]
π§ How to Approach This
Step 1 β Stack: Count stack + string stack. On '[': push. On ']': pop and repeat.
function decodeString(string $s): string {
$cntStack = []; $strStack = []; $curr = ''; $k = 0;
foreach (str_split($s) as $c) {
if (is_numeric($c)) { $k = $k * 10 + (int)$c; }
elseif ($c === '[') { $cntStack[] = $k; $strStack[] = $curr; $curr = ''; $k = 0; }
elseif ($c === ']') { $curr = array_pop($strStack) . str_repeat($curr, array_pop($cntStack)); }
else { $curr .= $c; }
}
return $curr;
}
echo decodeString("3[a]2[bc]"); // "aaabcbc"
echo decodeString("3[a2[c]]"); // "accaccacc"
Time: O(output size) | Space: O(n)
Q18. Word Break [M][LC-139]
π§ How to Approach This
Step 1 β Recursion + Memo: From index i, try every prefix. If in dictionary, recurse on rest.
function wordBreak(string $s, array $wordDict, int $start = 0, array &$memo = []): bool {
if ($start === strlen($s)) return true;
if (isset($memo[$start])) return $memo[$start];
$words = array_flip($wordDict);
for ($end = $start + 1; $end <= strlen($s); $end++) {
if (isset($words[substr($s, $start, $end - $start)]) &&
wordBreak($s, $wordDict, $end, $memo)) {
return $memo[$start] = true;
}
}
return $memo[$start] = false;
}
echo wordBreak("leetcode", ["leet","code"]) ? 'yes' : 'no'; // yes
echo wordBreak("applepenapple", ["apple","pen"]) ? 'yes' : 'no'; // yes
echo wordBreak("catsandog", ["cats","dog","sand"]) ? 'yes' : 'no'; // no
Time: O(nΒ²) | Space: O(n)
Q19. Palindrome Partitioning [M][LC-131]
π§ How to Approach This
Step 1 β Backtracking: Try each prefix. If palindrome β recurse on rest. Collect partitions.
function partition(string $s): array {
$result = []; $curr = [];
$isPalin = fn($x) => $x === strrev($x);
function bt(int $start, string $s, array &$curr, array &$result, callable $isPalin): void {
if ($start === strlen($s)) { $result[] = $curr; return; }
for ($end = $start + 1; $end <= strlen($s); $end++) {
$sub = substr($s, $start, $end - $start);
if ($isPalin($sub)) {
$curr[] = $sub;
bt($end, $s, $curr, $result, $isPalin);
array_pop($curr);
}
}
}
bt(0, $s, $curr, $result, $isPalin);
return $result;
}
print_r(partition("aab")); // [["a","a","b"],["aa","b"]]
Time: O(nΓ2βΏ) | Space: O(n)
Q20. Zigzag Conversion [M][LC-6]
π§ How to Approach This
Step 1 β Simulate: Use an array of numRows strings. Direction toggles at top/bottom row.
function convert(string $s, int $numRows): string {
if ($numRows === 1) return $s;
$rows = array_fill(0, $numRows, '');
$row = 0; $down = true;
foreach (str_split($s) as $c) {
$rows[$row] .= $c;
if ($row === 0) $down = true;
if ($row === $numRows - 1) $down = false;
$row += $down ? 1 : -1;
}
return implode('', $rows);
}
echo convert("PAYPALISHIRING", 3); // "PAHNAPLSIIGYIR"
echo convert("PAYPALISHIRING", 4); // "PINALSIGYAHRPI"
Time: O(n) | Space: O(n)
Q21. Longest Repeating Character Replacement [M][LC-424]
π§ How to Approach This
Step 1 β Sliding Window: Track max frequency in window. If
windowSize - maxFreq > kβ shrink left.
function characterReplacement(string $s, int $k): int {
$freq = array_fill(0, 26, 0);
$maxFreq = 0; $maxLen = 0; $lo = 0;
for ($r = 0; $r < strlen($s); $r++) {
$maxFreq = max($maxFreq, ++$freq[ord($s[$r]) - ord('A')]);
while (($r - $lo + 1) - $maxFreq > $k)
$freq[ord($s[$lo++]) - ord('A')]--;
$maxLen = max($maxLen, $r - $lo + 1);
}
return $maxLen;
}
echo characterReplacement("AABABBA", 1); // 4
echo characterReplacement("ABAB", 2); // 4
Time: O(n) | Space: O(1)
Q22. Minimum Deletions to Make Character Frequencies Unique [M][LC-1647]
π§ How to Approach This
Step 1 β Greedy: Count frequencies. Sort descending. For each freq, if already taken, reduce by 1 (deleting) until unique or 0.
function minDeletions(string $s): int {
$freq = array_fill(0, 26, 0);
foreach (str_split($s) as $c) $freq[ord($c) - ord('a')]++;
rsort($freq);
$used = []; $dels = 0;
foreach ($freq as $f) {
while ($f > 0 && in_array($f, $used)) { $f--; $dels++; }
if ($f > 0) $used[] = $f;
}
return $dels;
}
echo minDeletions("aab"); // 0
echo minDeletions("aaabbbcc"); // 2
Time: O(n + 26 log 26) | Space: O(1)
Q23. Check if Two Strings are Isomorphic [E][LC-205]
π§ How to Approach This
Step 1: Map each char in s to char in t. Check that mapping is consistent and bijective.
function isIsomorphic(string $s, string $t): bool {
$st = []; $ts = [];
for ($i = 0; $i < strlen($s); $i++) {
$a = $s[$i]; $b = $t[$i];
if ((isset($st[$a]) && $st[$a] !== $b) ||
(isset($ts[$b]) && $ts[$b] !== $a)) return false;
$st[$a] = $b; $ts[$b] = $a;
}
return true;
}
echo isIsomorphic("egg","add") ? 'yes' : 'no'; // yes
echo isIsomorphic("foo","bar") ? 'yes' : 'no'; // no
echo isIsomorphic("paper","title") ? 'yes' : 'no'; // yes
Time: O(n) | Space: O(1)
Q24. Largest Number from Array [M][LC-179]
π§ How to Approach This
Step 1 β Custom Sort: Compare $a.$b vs $b.$a as strings to decide order.
function largestNumber(array $nums): string {
usort($nums, fn($a,$b) => strcmp((string)$b.$a, (string)$a.$b));
$res = implode('', $nums);
return $res[0] === '0' ? '0' : $res;
}
echo largestNumber([3,30,34,5,9]); // "9534330"
echo largestNumber([10,2]); // "210"
Time: O(n log n) | Space: O(n)
Q25. Find All Anagrams in String [M][LC-438]
π§ How to Approach This
Step 1 β Fixed Sliding Window: Window size = len(p). Slide and compare frequency maps.
function findAnagrams(string $s, string $p): array {
$result = [];
$m = strlen($s); $k = strlen($p);
if ($m < $k) return [];
$pFreq = array_fill(0, 26, 0);
$wFreq = array_fill(0, 26, 0);
foreach (str_split($p) as $c) $pFreq[ord($c) - ord('a')]++;
for ($i = 0; $i < $k; $i++) $wFreq[ord($s[$i]) - ord('a')]++;
if ($pFreq === $wFreq) $result[] = 0;
for ($r = $k; $r < $m; $r++) {
$wFreq[ord($s[$r]) - ord('a')]++;
$wFreq[ord($s[$r - $k]) - ord('a')]--;
if ($pFreq === $wFreq) $result[] = $r - $k + 1;
}
return $result;
}
print_r(findAnagrams("cbaebabacd", "abc")); // [0, 6]
print_r(findAnagrams("abab", "ab")); // [0, 1, 2]
Time: O(n) | Space: O(1)
Pattern Summary
| Technique | Problems |
|---|---|
| Two Pointers | Palindrome, Reverse String, Reverse Vowels |
| Sliding Window | Longest No-Repeat, Min Window, Longest Repeat Replace, Find Anagrams |
| Frequency Map | Valid Anagram, Group Anagrams, First Unique Char |
| Stack | Valid Parentheses, Decode String |
| Expand Center | Longest Palindromic Substring |
| Simulation | Count&Say, Zigzag |
| Greedy | Integer to Roman, Largest Number, Min Deletions |
| Recursion+Memo | Word Break, Palindrome Partition |