Text that owns and measures itself
A large share of interview problems are string problems, including parsing, palindromes, anagrams, and tokenizing. In production, text handling is where C's raw char arrays with manual length tracking caused decades of buffer-overflow security bugs.
std::string exists to close that hole. It owns its memory, knows its size, and grows on demand, so there is no separate length variable to keep in sync and no fixed buffer to overrun.
A string is a vector of chars, with extras
std::string manages a heap character array with RAII, exactly like vector, and shares much of its interface: s.size(), s[i], s.empty(), s.push_back('x'), s.back(). On top of that it adds text-specific operations:
std::string s = "hello"; s + " world" // concatenation (makes a new string) s += "!"; // append in place s.substr(1, 3) // "ell" : from index 1, take 3 chars s.find("ll") // 2 : index of first match s.find("zz") // std::string::npos : the not-found marker s == "hello" // true : compares contents std::to_string(42) // "42" std::stoi("42") // 42
Note that == compares contents, unlike C arrays where it would compare addresses. Note also that substr takes a start and a length, not a start and an end, which is a common source of off-by-one errors for people arriving from Python slices.
find returning std::string::npos deserves care, because npos is a huge unsigned value rather than -1. A test must be written as if (s.find(t) != std::string::npos), and comparing the result against a signed -1 is a bug.
Characters are small numbers
s[i] is a char, and chars are integers underneath, holding their ASCII codes. That enables the arithmetic interviews love:
char c = 'b'; c - 'a' // 1 : position in the alphabet char(c + 1) // 'c' '5' - '0' // 5 : digit character to number
Reading strings
std::cin >> word reads one whitespace-delimited word, as lesson 1-2 covered. To read a whole line including spaces, use std::getline(std::cin, line).
Slicing, searching, and char arithmetic
Four operations on one string, with the results printed so the index conventions are visible.
#include <iostream> #include <string> int main() { std::string s = "interview"; std::cout << "size: " << s.size() << "\n"; std::cout << "substr(0, 5): " << s.substr(0, 5) << "\n"; std::cout << "find('v'): " << s.find('v') << "\n"; char first = s[0]; std::cout << "alphabet position of '" << first << "': " << first - 'a' << "\n"; return 0; }
Output
size: 9 substr(0, 5): inter find('v'): 5 alphabet position of 'i': 8
substr(0, 5) returns the five characters starting at index 0, giving inter. Reading the second argument as an end index would predict inter too, which is why this call is a poor test of understanding, while substr(1, 3) on "hello" gives ell rather than el and settles it.
find('v') returns 5, and counting i-n-t-e-r-v confirms that v sits at index 5 in a 0-based count. The result is an index, not a boolean, so using it directly in an if would be wrong in two ways, since index 0 is a real match that reads as false.
The last line prints 8 because 'i' is the ninth letter of the alphabet and the subtraction is 0-based. Both operands are char values, and their difference is an int, which is why the stream prints a number here rather than a character.
Converting a digit character to its value
The expression '7' - '0' evaluates to the int 7, because the character codes for digits are consecutive.
Digits '0' through '9' occupy ten consecutive character codes, 48 through 57 in ASCII, so subtracting '0' from any digit character yields its numeric value. Nothing about the specific value 48 needs remembering, since the subtraction cancels it out.
| Expression | Result | Type |
|---|---|---|
'7' | the character 7 | char |
'7' - '0' | 7 | int |
'7' + 1 | 56 | int |
char('7' + 1) | the character 8 | char |
'c' - 'a' | 2 | int |
The same trick with 'a' gives a letter's position in the alphabet, and it works for the same reason, since the lowercase letters are also consecutive. It is worth knowing that uppercase letters occupy a different consecutive run, so 'C' - 'a' is a meaningless negative number and case has to be normalized first.
You will use this constantly when parsing strings by hand, and the guard that belongs with it is a range check like if (c >= '0' && c <= '9'), or the std::isdigit function from <cctype>.
Reversing a word and counting its vowels
Two passes over one string, the first walking backwards to print and the second walking forwards to count.
#include <iostream> #include <string> int main() { std::string w; std::cin >> w; for (int i = (int)w.size() - 1; i >= 0; i--) { std::cout << w[i]; } std::cout << "\n"; int count = 0; for (int i = 0; i < (int)w.size(); i++) { char c = w[i]; if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') { count++; } } std::cout << "vowels: " << count << "\n"; return 0; }
Input
algorithm
Output
mhtirogla
vowels: 3The newline comes after the reverse loop rather than inside it, which is what keeps the reversed word on a single line. Putting it inside would print one character per line, and the mistake is easy to make because the loop body is a single statement without braces of its own.
The reverse loop prints without building a new string, which is the cheaper approach when the reversed text is only needed for output. Building it instead would use std::string r(w.rbegin(), w.rend()); or std::reverse(w.begin(), w.end()) from lesson 9-2.
The three vowels in algorithm are a, o, and i. Only lowercase vowels are tested, so an input of ALGORITHM would report zero, which is the kind of unstated assumption worth naming out loud in an interview.
The vowel count on a fixed string
The same counting loop with the input hardcoded, which isolates the comparison chain.
#include <iostream> #include <string> int main() { std::string s = "encyclopedia"; int vowels = 0; for (int i = 0; i < (int)s.size(); i++) { char c = s[i]; if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') { vowels++; } } std::cout << vowels << "\n"; return 0; }
Output
5The five vowels in encyclopedia are e, o, e, i, and a, with the y deliberately not counted. Each comparison is spelled out and joined with ||, and the single quotes matter, since "a" with double quotes is a string and comparing a char against it does not compile.
Copying s[i] into a named char c costs nothing and keeps the condition readable. Written inline, the test would repeat s[i] five times, which is both noisier and easier to typo.
A shorter alternative uses the string's own search: if (std::string("aeiou").find(c) != std::string::npos). It is more compact and scales better to a larger set of characters, at the cost of scanning up to five characters per test rather than short-circuiting on the first match.
The (int)s.size() cast avoids the signed and unsigned comparison warning from lesson 8-1, and the same reasoning applies here as it did there.