Problem overview
The function onlyInFirst gets two vectors of integers, a and b, and returns a new vector with the values of a that are not in b. Each value goes into the result once, in the order it first appears in a. If every value of a is also in b, the result is empty.
For a = {1, 2, 3, 4} and b = {2, 4} the answer is 1 3. For a = {5, 6, 5, 7} and b = {7} it is 5 6: 7 is in b, and 5 appears twice in a but once in the result. For a = {1, 2} and b = {2, 3, 1} the answer is empty. The tests print a vector as its values separated by spaces.
Approach
Two questions are asked about each number of a: is it in b, and is it already in the result? A vector answers each question by scanning it. A set answers with one lookup, so the code uses two sets:
setBholds the values ofb. Repeats inbcollapse into one, which doesn't matter for a membership test.seenholds the values already put in the result, which stops a repeated value ofafrom going in twice. It is the same idea as removing repeats from a single vector.
The result vector itself keeps the order.
Two sets are not always needed. If the task allowed the result in any order, one set would be enough. The order requirement is the reason the result is a vector, and the vector is the reason the second set exists.
Step by step
- Build
setBfromb.begin()andb.end(). - Create an empty
seenset ofint. - Create an empty
resultvector ofint. - Loop over
afrom left to right with anintnamednum, so the first appearance decides the order. - Test both conditions:
setB.count(num) == 0andseen.count(num) == 0, joined with&&. Both must hold. - Add
numto the end ofresultwithpush_back. - Add
numtoseenwithinsert. - Return
resultafter the loop. It can be empty.
The solution in C++
class Solution {
public:
vector<int> onlyInFirst(vector<int>& a, vector<int>& b) {
unordered_set<int> setB(b.begin(), b.end());
unordered_set<int> seen;
vector<int> result;
for (int num : a) {
if (setB.count(num) == 0 && seen.count(num) == 0) {
result.push_back(num);
seen.insert(num);
}
}
return result;
}
};
Notes on the C++:
- If the numbers 5, 6, 5 and 7 are checked against
b = {7}, the first 5 and the 6 pass both tests; the second 5 fails theseentest; the 7 fails thesetBtest. &&stops at the first false test, soseenis only consulted for values that are not inb.
Complexity
Time is O(n + m) on average, where n is the size of a and m the size of b: building setB costs m inserts and the loop costs n constant-time lookups. Space is O(n + m) for the two sets and the result.
Edge cases and common mistakes
- Dropping the
seencheck. For{5, 6, 5, 7}and{7}the result would be5 6 5, which breaks "each value once". - Using
||instead of&&. A value that is inbbut not yet seen would still be added. - Checking
aagainstbwith nested loops. It gives the right answer but scansbfor every number. - Everything removed. When all values of
aare inb, nothing is added and the empty vector is returned, which is correct.
Testing
a = {1, 2, 3, 4},b = {2, 4}gives1 3.a = {5, 6, 5, 7},b = {7}gives5 6.a = {1, 2},b = {2, 3, 1}gives an empty vector.
