🎯 Top 25 PHP String Interview Questions

Parent: Strings PHP Index


Quick Reference

#ProblemDifficultyPatternTime
1Reverse StringETwo PointersO(n)
2Valid PalindromeETwo PointersO(n)
3Valid AnagramEFrequency MapO(n)
4First Unique CharacterEFrequency MapO(n)
5Reverse WordsESplit + ReverseO(n)
6String CompressionMTwo PointersO(n)
7Valid ParenthesesEStackO(n)
8Longest Substring No RepeatMSliding WindowO(n)
9Longest Palindromic SubstringMExpand CenterO(nΒ²)
10Group AnagramsMHash MapO(nΒ·k log k)
11Implement strStr (KMP)EKMPO(n+m)
12Count & SayMSimulationO(n·2ⁿ)
13Minimum Window SubstringHSliding WindowO(n+m)
14Encode/Decode StringsMDelimiterO(n)
15Longest Common PrefixEHorizontal ScanO(nΒ·m)
16Roman to IntegerEHash MapO(n)
17Integer to RomanMGreedyO(1)
18Decode StringMStackO(n)
19Word BreakMRecursion + MemoO(nΒ²)
20Palindrome PartitioningMBacktrackingO(n·2ⁿ)
21Scramble StringHRecursion + MemoO(n⁴)
22Zigzag ConversionMSimulationO(n)
23Wildcard MatchingHDPO(mΓ—n)
24Longest Repeating Char ReplaceMSliding WindowO(n)
25Minimum Deletions to Make Freq UniqueMGreedyO(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 w and read pointer i.

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 strpos is 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 need map and formed count. 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

TechniqueProblems
Two PointersPalindrome, Reverse String, Reverse Vowels
Sliding WindowLongest No-Repeat, Min Window, Longest Repeat Replace, Find Anagrams
Frequency MapValid Anagram, Group Anagrams, First Unique Char
StackValid Parentheses, Decode String
Expand CenterLongest Palindromic Substring
SimulationCount&Say, Zigzag
GreedyInteger to Roman, Largest Number, Min Deletions
Recursion+MemoWord Break, Palindrome Partition