Skip to content
DSA arrays 11 min read

String Exercises: Answer Sheet & Worked Solutions

This is the answer sheet for the string exercises. Each solution gives the key idea, the optimal complexity, the code in all four languages (use the switcher), and common pitfalls. Try the problems first — then check your work here.

Q1. Reverse a string

Idea: Build a character buffer you can mutate (or use two pointers on one), swap ends inward, then join. C++ strings are mutable; Java/JS/Python need a copy because their strings are immutable.

Complexity: O(n) time, O(n) space (output string).

#include <string>
#include <algorithm>
std::string reverseString(std::string s) {
    std::reverse(s.begin(), s.end()); // in place, s is mutable
    return s;
}

Pitfall: Trying s[i] = ... on a Java, JavaScript, or Python string fails or silently does nothing — they are immutable. Convert to an array/builder first.

Q2. Check a palindrome

Idea: Two pointers from both ends moving inward; if any pair differs, it’s not a palindrome. No extra copy needed.

Complexity: O(n) time, O(1) extra space.

#include <string>
bool isPalindrome(const std::string& s) {
    int i = 0, j = (int)s.size() - 1;
    while (i < j) {
        if (s[i] != s[j]) return false;
        i++; j--;
    }
    return true;
}

Pitfall: If the problem says “ignore case and non-alphanumeric characters,” normalize first (lowercase + filter) or skip non-alphanumerics with the two pointers. The bare version above compares raw characters.

Q3. Count vowels

Idea: Single pass; check membership in a small vowel set (lowercase the character to handle both cases).

Complexity: O(n) time, O(1) space.

#include <string>
#include <cctype>
int countVowels(const std::string& s) {
    int count = 0;
    for (char c : s) {
        char l = std::tolower((unsigned char)c);
        if (l == 'a' || l == 'e' || l == 'i' || l == 'o' || l == 'u') count++;
    }
    return count;
}

Pitfall: Forgetting case — counting only lowercase misses A, E, etc. Normalize case before checking.

Q4. Valid anagram

Idea: Two strings are anagrams iff they have the same character counts. Tally one string up and the other down in a single frequency table; all zeros means a match. Different lengths can’t match.

Complexity: O(n) time, O(k) space (k = alphabet size).

#include <string>
#include <unordered_map>
bool isAnagram(const std::string& a, const std::string& b) {
    if (a.size() != b.size()) return false;
    std::unordered_map<char, int> freq;
    for (char c : a) freq[c]++;
    for (char c : b) {
        if (--freq[c] < 0) return false;
    }
    return true;
}

Pitfall: Sorting both strings and comparing also works but is O(n log n). The frequency-count approach is O(n). Also remember the length short-circuit.

Q5. First unique character

Idea: Two passes. First count every character’s frequency; then scan left to right and return the index of the first character with count 1.

Complexity: O(n) time, O(k) space.

#include <string>
#include <unordered_map>
int firstUniqChar(const std::string& s) {
    std::unordered_map<char, int> freq;
    for (char c : s) freq[c]++;
    for (int i = 0; i < (int)s.size(); i++)
        if (freq[s[i]] == 1) return i;
    return -1;
}

Pitfall: Returning the first character whose count you haven’t seen again yet in one pass is wrong — you must finish counting before deciding uniqueness. Two passes (or count then scan) is the clean way.

Q6. Most frequent character

Idea: Build a frequency table, then take the character with the maximum count. Ties can be broken arbitrarily (here, the first one to reach the max).

Complexity: O(n) time, O(k) space.

#include <string>
#include <unordered_map>
char mostFrequent(const std::string& s) {
    std::unordered_map<char, int> freq;
    char best = s[0]; int bestCount = 0;
    for (char c : s) {
        int cnt = ++freq[c];
        if (cnt > bestCount) { bestCount = cnt; best = c; }
    }
    return best;
}

Pitfall: Calling this on an empty string indexes s[0] out of bounds. Validate non-empty input (or define a policy) before calling.

Q7. Longest substring without repeating characters

Idea: A sliding window with a “last seen index” map. Expand the right end one character at a time; when you hit a repeat that’s inside the window, jump the left end just past its previous position. Track the max window length. Each character is visited at most twice.

Complexity: O(n) time, O(k) space.

#include <string>
#include <unordered_map>
#include <algorithm>
int lengthOfLongest(const std::string& s) {
    std::unordered_map<char, int> last;
    int best = 0, left = 0;
    for (int right = 0; right < (int)s.size(); right++) {
        char c = s[right];
        if (last.count(c) && last[c] >= left) left = last[c] + 1;
        last[c] = right;
        best = std::max(best, right - left + 1);
    }
    return best;
}

Pitfall: Only move left forward, never backward. The guard last[c] >= left ensures a repeat outside the current window doesn’t wrongly shrink it.


That’s the full answer sheet. Back to the string exercises or continue to linked lists.

Last updated June 25, 2026
Was this helpful?