LeetCode 2053 - Kth Distinct String in an Array
The problem asks us to find the kth distinct string in an array of strings. A string is considered distinct if it appears exactly once in the entire array. The important detail is that the order matters. We are not sorting the strings or rearranging them in any way.
Difficulty: 🟢 Easy
Topics: Array, Hash Table, String, Counting
Solution
Problem Understanding
The problem asks us to find the kth distinct string in an array of strings. A string is considered distinct if it appears exactly once in the entire array.
The important detail is that the order matters. We are not sorting the strings or rearranging them in any way. Instead, we must consider distinct strings in the same order they originally appear in the array.
For example, if the array is:
["d","b","c","b","c","a"]
then:
"d"appears once, so it is distinct"b"appears twice, so it is not distinct"c"appears twice, so it is not distinct"a"appears once, so it is distinct
The distinct strings, in order, are:
["d", "a"]
If k = 2, the answer is "a".
The input consists of:
arr, an array of lowercase stringsk, a positive integer
The output should be:
- the
kthdistinct string in appearance order - or an empty string
""if fewer thankdistinct strings exist
The constraints are relatively small:
arr.length <= 1000- each string length is at most
5
These constraints mean even a quadratic solution would technically pass, but we should still aim for a clean and efficient approach.
Several edge cases are important:
- Arrays where no strings are distinct
- Arrays where every string is distinct
- Cases where
kis larger than the number of distinct strings - Repeated strings appearing many times
- Very small arrays, including length
1
A correct solution must preserve original order while accurately counting occurrences.
Approaches
Brute Force Approach
The brute-force method checks every string individually and counts how many times it appears in the array.
For each string:
- Scan the entire array again
- Count occurrences
- If the count is exactly
1, it is distinct - Keep track of how many distinct strings have been found
- Return the
kthone
This approach is correct because it explicitly verifies the occurrence count for every string. However, it repeatedly scans the array for each element, which is inefficient.
If the array has n elements, and each element requires another full scan of n, the total time complexity becomes O(n²).
Although n <= 1000 is small enough for this to pass, it is unnecessary work.
Optimal Approach
The key observation is that we repeatedly compute the same frequency counts.
Instead of recounting each string many times, we can count all occurrences once using a hash map.
The algorithm works in two passes:
- First pass:
- Count how many times each string appears
- Second pass:
- Iterate through the original array in order
- Select only strings with frequency
1 - Return the
kthsuch string
A hash map is ideal here because it provides average O(1) lookup and insertion time.
This reduces the overall time complexity to linear time.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(1) | Recounts occurrences for every string |
| Optimal | O(n) | O(n) | Uses a hash map for frequency counting |
Algorithm Walkthrough
Optimal Algorithm
- Create a hash map called
frequency.
This map stores how many times each string appears in the array. The key is the string, and the value is its occurrence count. 2. Traverse the array once to build frequencies.
For every string s in arr:
- Increment
frequency[s]
After this step, we know exactly how many times every string appears. 3. Traverse the array again in original order.
We must preserve the order of appearance, so we iterate through arr exactly as given.
4. Check whether the current string is distinct.
A string is distinct if:
frequency[s] == 1
- Count distinct strings as they appear.
Each time we find a distinct string:
- Decrement
k
- Return the answer when
k == 0.
Once we have encountered the kth distinct string, return it immediately.
7. If the traversal finishes without finding enough distinct strings, return "".
Why it works
The frequency map guarantees that we correctly identify whether a string is distinct. The second traversal preserves the original ordering required by the problem. Since we decrement k only when encountering distinct strings, the moment k reaches zero corresponds exactly to the kth distinct string in order.
Python Solution
from typing import List
from collections import Counter
class Solution:
def kthDistinct(self, arr: List[str], k: int) -> str:
frequency = Counter(arr)
for string in arr:
if frequency[string] == 1:
k -= 1
if k == 0:
return string
return ""
The implementation begins by using Counter from Python's collections module. This automatically computes the frequency of every string in the array.
frequency = Counter(arr)
For example:
Counter({
"b": 2,
"c": 2,
"d": 1,
"a": 1
})
Next, we iterate through the array in its original order.
for string in arr:
For each string, we check whether its frequency is exactly 1.
if frequency[string] == 1:
If it is distinct, we decrement k.
k -= 1
When k becomes zero, we have reached the kth distinct string.
if k == 0:
return string
If the loop finishes without returning, there are fewer than k distinct strings, so we return an empty string.
return ""
Go Solution
func kthDistinct(arr []string, k int) string {
frequency := make(map[string]int)
for _, str := range arr {
frequency[str]++
}
for _, str := range arr {
if frequency[str] == 1 {
k--
if k == 0 {
return str
}
}
}
return ""
}
The Go solution follows exactly the same logic as the Python implementation.
A map[string]int is used to store frequencies.
frequency := make(map[string]int)
The first loop counts occurrences.
frequency[str]++
The second loop preserves original order and identifies distinct strings.
Go strings are immutable and comparable, so they work naturally as hash map keys.
Unlike Python, Go does not have a built-in Counter, so we manually increment counts.
Returning "" is straightforward because an empty string is the zero value for strings in Go.
Worked Examples
Example 1
Input:
arr = ["d","b","c","b","c","a"]
k = 2
Step 1, Build Frequency Map
| String | Count |
|---|---|
| d | 1 |
| b | 2 |
| c | 2 |
| a | 1 |
Step 2, Traverse in Order
| Index | String | Frequency | Distinct? | k After Processing |
|---|---|---|---|---|
| 0 | d | 1 | Yes | 1 |
| 1 | b | 2 | No | 1 |
| 2 | c | 2 | No | 1 |
| 3 | b | 2 | No | 1 |
| 4 | c | 2 | No | 1 |
| 5 | a | 1 | Yes | 0 |
When k becomes 0, return "a".
Example 2
Input:
arr = ["aaa","aa","a"]
k = 1
Frequency Map
| String | Count |
|---|---|
| aaa | 1 |
| aa | 1 |
| a | 1 |
Traversal
| Index | String | Frequency | Distinct? | k After Processing |
|---|---|---|---|---|
| 0 | aaa | 1 | Yes | 0 |
Return "aaa" immediately.
Example 3
Input:
arr = ["a","b","a"]
k = 3
Frequency Map
| String | Count |
|---|---|
| a | 2 |
| b | 1 |
Traversal
| Index | String | Frequency | Distinct? | k After Processing |
|---|---|---|---|---|
| 0 | a | 2 | No | 3 |
| 1 | b | 1 | Yes | 2 |
| 2 | a | 2 | No | 2 |
The traversal ends before k reaches 0, so return:
""
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | One pass to count frequencies and one pass to find the answer |
| Space | O(n) | Hash map may store all unique strings |
The algorithm performs two linear scans of the array. Hash map operations are average O(1), so the total runtime remains linear.
The extra space comes from storing frequencies of distinct strings. In the worst case, every string is unique, requiring O(n) additional space.
Test Cases
solution = Solution()
# Provided examples
assert solution.kthDistinct(["d","b","c","b","c","a"], 2) == "a" # second distinct string
assert solution.kthDistinct(["aaa","aa","a"], 1) == "aaa" # all strings distinct
assert solution.kthDistinct(["a","b","a"], 3) == "" # fewer than k distinct strings
# Single element array
assert solution.kthDistinct(["x"], 1) == "x" # smallest valid input
# No distinct strings
assert solution.kthDistinct(["a","a","b","b"], 1) == "" # all repeated
# Exactly one distinct string
assert solution.kthDistinct(["a","b","a"], 1) == "b" # only one distinct value
# Distinct strings appear later
assert solution.kthDistinct(["x","x","y","z"], 2) == "z" # second distinct near end
# k equals number of distinct strings
assert solution.kthDistinct(["a","b","c"], 3) == "c" # last distinct string
# Repeated strings many times
assert solution.kthDistinct(["a","a","a","b","c","c"], 1) == "b" # one unique among many duplicates
# Multiple distinct strings
assert solution.kthDistinct(["p","q","r","p","s"], 3) == "s" # third distinct string
# k larger than distinct count
assert solution.kthDistinct(["a","b","b"], 2) == "" # only one distinct string exists
| Test | Why |
|---|---|
["d","b","c","b","c","a"], k=2 |
Validates normal mixed case |
["aaa","aa","a"], k=1 |
Validates all unique strings |
["a","b","a"], k=3 |
Validates insufficient distinct strings |
["x"], k=1 |
Smallest possible input |
["a","a","b","b"], k=1 |
Validates no distinct strings |
["a","b","a"], k=1 |
Validates exactly one distinct string |
["x","x","y","z"], k=2 |
Ensures order is preserved |
["a","b","c"], k=3 |
Validates last distinct element |
["a","a","a","b","c","c"], k=1 |
Handles many duplicates |
["p","q","r","p","s"], k=3 |
Validates counting multiple distinct strings |
["a","b","b"], k=2 |
Validates k exceeding distinct count |
Edge Cases
Case 1, No Distinct Strings
Consider:
["a", "a", "b", "b"]
Every string appears more than once. A buggy implementation might incorrectly return the first element or fail to handle the absence of valid answers.
Our implementation correctly checks:
frequency[string] == 1
Since no string satisfies this condition, the loop completes and returns "".
Case 2, All Strings Are Distinct
Consider:
["x", "y", "z"]
Every string appears exactly once. This tests whether the algorithm preserves original order correctly.
Our implementation processes the array sequentially and decrements k each time a distinct string is found, so the order remains accurate.
Case 3, k Larger Than the Number of Distinct Strings
Consider:
["a", "b", "a"]
k = 5
There is only one distinct string, "b".
A careless implementation might access invalid indices or assume the answer always exists.
Our algorithm safely traverses the entire array. If k never reaches 0, it returns an empty string at the end.
Case 4, Distinct String Appears Late
Consider:
["x", "x", "x", "y"]
The only distinct string appears at the very end.
This validates that the algorithm continues scanning the entire array and does not stop early when duplicates dominate the beginning.
Because we perform a complete second pass through the array, the implementation correctly identifies "y" as the answer.