# 1002. Find Common Characters
LeetCode Problem Link (opens new window)
Given a string array words, return an array of all characters that show up in all strings within the words (including duplicates). You may return the answer in any order.
Example 1:
Input: words = ["bella","label","roller"]
Output: ["e","l","l"]
Example 2:
Input: words = ["cool","lock","cook"]
Output: ["c","o"]
Constraints:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] consist of lowercase English letters.
# Approach
The problem statement is somewhat tricky at first, but it essentially asks for characters that appear in every string within the array, including duplicate occurrences.
For instance:
Input: ["ll","ll","ll"]
Output: ["l","l"]
Upon first glance, this problem appears to be tailor-made for a hash map approach. Keywords such as "lowercase character" and "frequency" clearly suggest the use of a hash map.
One might first consider a brute force solution, processing one string at a time, leading to a time complexity of (O(n^m)), where (n) is the length of the string and (m) is the number of strings.
Clearly, this is an exponential time complexity, which is quite high. Furthermore, the code implementation isn't straightforward due to the need to handle duplicate characters and replacements accordingly.
Thus, it's preferable to use a hash map approach. If you're unfamiliar with hash maps, you can refer to this article: Hash Table Basics (opens new window).
If you are not familiar with using arrays as hash maps, please check out this article: 0242.Valid Anagram (opens new window).
Once you're familiar with hash maps and their usage in arrays, we can proceed to the solution strategy.
The overall strategy is to count the frequency of each of the 26 characters in every word and then take the minimum frequency of each character. Finally, convert these into the required output format.
For illustration:
First, count the occurrence of every character in the first string. The code is as follows:
int hash[26] = {0}; // Used to count the minimal frequency of characters across all strings
for (int i = 0; i < A[0].size(); i++) { // Initialize hash using the first string
hash[A[0][i] - 'a']++;
}
2
3
4
Next, count the frequency of characters in the remaining strings in hashOtherStr.
Then, take the minimum value between hash and hashOtherStr. This is the key step—taking the minimum here represents a character's minimum occurrence across all strings.
Here's the code:
int hashOtherStr[26] = {0}; // Count the frequency of characters in strings, excluding the first
for (int i = 1; i < A.size(); i++) {
memset(hashOtherStr, 0, 26 * sizeof(int));
for (int j = 0; j < A[i].size(); j++) {
hashOtherStr[A[i][j] - 'a']++;
}
// This is crucial
for (int k = 0; k < 26; k++) { // Update hash to keep the minimum count across strings for 26 characters
hash[k] = min(hash[k], hashOtherStr[k]);
}
}
2
3
4
5
6
7
8
9
10
11
At this point, hash contains the minimum frequency for any character that appears in all strings. Now, convert hash into the desired output format.
Here's the code:
// Convert the character counts in hash to the output format
for (int i = 0; i < 26; i++) {
while (hash[i] != 0) { // Note the use of 'while' for multiple repeated characters
string s(1, i + 'a'); // Convert char to string
result.push_back(s);
hash[i]--;
}
}
2
3
4
5
6
7
8
The complete C++ code:
class Solution {
public:
vector<string> commonChars(vector<string>& A) {
vector<string> result;
if (A.size() == 0) return result;
int hash[26] = {0}; // Used to count the minimal frequency of characters across all strings
for (int i = 0; i < A[0].size(); i++) { // Initialize hash using the first string
hash[A[0][i] - 'a']++;
}
int hashOtherStr[26] = {0}; // Count the frequency of characters in strings, excluding the first
for (int i = 1; i < A.size(); i++) {
memset(hashOtherStr, 0, 26 * sizeof(int));
for (int j = 0; j < A[i].size(); j++) {
hashOtherStr[A[i][j] - 'a']++;
}
// Update hash to keep the minimum count across strings for 26 characters
for (int k = 0; k < 26; k++) {
hash[k] = min(hash[k], hashOtherStr[k]);
}
}
// Convert the character counts in hash to the output format
for (int i = 0; i < 26; i++) {
while (hash[i] != 0) { // Note the use of 'while' for multiple repeated characters
string s(1, i + 'a'); // Convert char to string
result.push_back(s);
hash[i]--;
}
}
return result;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Other Language Versions
# Java:
class Solution {
public List<String> commonChars(String[] A) {
List<String> result = new ArrayList<>();
if (A.length == 0) return result;
int[] hash= new int[26]; // Used to count the minimal frequency of characters across all strings
for (int i = 0; i < A[0].length(); i++) { // Initialize hash using the first string
hash[A[0].charAt(i)- 'a']++;
}
// Count the frequency of characters in strings, excluding the first
for (int i = 1; i < A.length; i++) {
int[] hashOtherStr= new int[26];
for (int j = 0; j < A[i].length(); j++) {
hashOtherStr[A[i].charAt(j)- 'a']++;
}
// Update hash to keep the minimum count across strings for 26 characters
for (int k = 0; k < 26; k++) {
hash[k] = Math.min(hash[k], hashOtherStr[k]);
}
}
// Convert the character counts in hash to the output format
for (int i = 0; i < 26; i++) {
while (hash[i] != 0) { // Note the use of 'while' for multiple repeated characters
char c= (char) (i+'a');
result.add(String.valueOf(c));
hash[i]--;
}
}
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Python
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
if not words: return []
result = []
hash = [0] * 26 # Used to count the minimal frequency of characters across all strings
for i, c in enumerate(words[0]): # Initialize hash using the first string
hash[ord(c) - ord('a')] += 1
# Count the frequency of characters in strings, excluding the first
for i in range(1, len(words)):
hashOtherStr = [0] * 26
for j in range(len(words[i])):
hashOtherStr[ord(words[i][j]) - ord('a')] += 1
# Update hash to keep the minimum count across strings for 26 characters
for k in range(26):
hash[k] = min(hash[k], hashOtherStr[k])
# Convert the character counts in hash to the output format
for i in range(26):
while hash[i] != 0: # Note the use of 'while' for multiple repeated characters
result.extend(chr(i + ord('a')))
hash[i] -= 1
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Python 3 using collections.Counter:
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
tmp = collections.Counter(words[0])
l = []
for i in range(1,len(words)):
# Use & to take intersection
tmp = tmp & collections.Counter(words[i])
# The remaining keys and values are the characters and their counts that appear in every word
for j in tmp:
v = tmp[j]
while(v):
l.append(j)
v -= 1
return l
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# JavaScript
var commonChars = function (words) {
let res = []
let size = 26
let firstHash = new Array(size).fill(0) // Initialize hash array
let a = "a".charCodeAt()
let firstWord = words[0]
for (let i = 0; i < firstWord.length; i++) { // Count first word
let idx = firstWord[i].charCodeAt()
firstHash[idx - a] += 1
}
let otherHash = new Array(size).fill(0) // Initialize other hash array
for (let i = 1; i < words.length; i++) { // Count words 1 to n
for (let j = 0; j < words[i].length; j++) {
let idx = words[i][j].charCodeAt()
otherHash[idx - a] += 1
}
for (let i = 0; i < size; i++) {
firstHash[i] = Math.min(firstHash[i], otherHash[i])
}
otherHash.fill(0)
}
for (let i = 0; i < size; i++) {
while (firstHash[i] > 0) {
res.push(String.fromCharCode(i + a))
firstHash[i]--
}
}
return res
};
// Method 2: Using map()
var commonChars = function(words) {
let min_count = new Map()
// Count the minimal frequency of characters by initializing map with first string
for(let str of words[0]) {
min_count.set(str, ((min_count.get(str) || 0) + 1))
}
// Count the frequency of characters in strings from the second string
for(let i = 1; i < words.length; i++) {
let char_count = new Map()
for(let str of words[i]) { // Traverse characters
char_count.set(str, (char_count.get(str) || 0) + 1)
}
// Determine the minimal character count
for(let value of min_count) { // Important to traverse min_count! Not the word
min_count.set(value[0], Math.min((min_count.get(value[0]) || 0), (char_count.get(value[0]) || 0)))
}
}
// Traverse map
let res = []
min_count.forEach((value, key) => {
if(value) {
for(let i=0; i<value; i++) {
res.push(key)
}
}
})
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# TypeScript
console.time("test")
let str: string = ""
// Create a map dictionary with letters
let map = new Map<string, number>()
// Initialize all map to 0
let wordInitial: [string, number][] = words[0]
.split("")
.map((item) => [item, 0])
// If there is a duplicate letter, increment its count
for (let word of words[0]) {
map.set(word, map.has(word) ? map.get(word) + 1 : 1)
}
for (let i = 1; i < words.length; i++) {
const mapWord = new Map<string, number>(wordInitial)
for (let j = 0; j < words[i].length; j++) {
if (!map.has(words[i][j])) continue
// The count of letters in mapWord should not exceed the current count in map
if (map.get(words[i][j]) > mapWord.get(words[i][j])) {
mapWord.set(
words[i][j],
mapWord.has(words[i][j]) ? mapWord!.get(words[i][j]) + 1 : 1
)
}
}
// Reinitialize map each time
map = mapWord
}
for (let [key, value] of map) {
str += key.repeat(value)
}
console.timeEnd("test")
return str.split("")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# GO
func commonChars(A []string) []string {
var result []string
if len(A) == 0 {
return result
}
hash := make([]int, 26) // Used to count the minimal frequency of characters across all strings
for _, c := range A[0] { // Initialize hash using the first string
hash[c-'a']++
}
for i := 1; i < len(A); i++ {
hashOtherStr := make([]int, 26) // Count the frequency of characters in strings, excluding the first
for _, c := range A[i] {
hashOtherStr[c-'a']++
}
// Update hash to keep the minimum count across strings for 26 characters
for k := 0; k < 26; k++ {
hash[k] = min(hash[k], hashOtherStr[k])
}
}
// Convert the character counts in hash to the output format
for i := 0; i < 26; i++ {
for hash[i] > 0 {
s := string('a' + i) // Convert rune to string
result = append(result, s)
hash[i]--
}
}
return result
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Swift:
func commonChars(_ words: [String]) -> [String] {
var res = [String]()
if words.count < 1 {
return res
}
let aUnicodeScalarValue = "a".unicodeScalars.first!.value
let lettersMaxCount = 26
// Used to count the minimal frequency of characters across all strings
var hash = Array(repeating: 0, count: lettersMaxCount)
// Count the occurrence of each character in the first string
for unicodeScalar in words.first!.unicodeScalars {
hash[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1
}
// Count the frequency of characters in the other strings
for idx in 1 ..< words.count {
var hashOtherStr = Array(repeating: 0, count: lettersMaxCount)
for unicodeScalar in words[idx].unicodeScalars {
hashOtherStr[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1
}
// Update hash to keep the minimum count across strings for 26 characters
for k in 0 ..< lettersMaxCount {
hash[k] = min(hash[k], hashOtherStr[k])
}
}
-- Convert the character counts in hash to the output format
for i in 0 ..< lettersMaxCount {
while hash[i] != 0 { // Note the use of 'while' for multiple repeated characters
let currentUnicodeScalarValue: UInt32 = UInt32(i) + aUnicodeScalarValue
let currentUnicodeScalar: UnicodeScalar = UnicodeScalar(currentUnicodeScalarValue)!
let outputStr = String(currentUnicodeScalar) // Convert UnicodeScalar to String
res.append(outputStr)
hash[i] -= 1
}
}
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# C:
// If two hash tables are defined as char arrays, the efficiency of both time and space can be improved
void updateHashTable(int* hashTableOne, int* hashTableTwo) {
int i;
for(i = 0; i < 26; i++) {
hashTableOne[i] = hashTableOne[i] < hashTableTwo[i] ? hashTableOne[i] : hashTableTwo[i];
}
}
char ** commonChars(char ** words, int wordsSize, int* returnSize){
// To count the minimal frequency of characters across all strings
int hashTable[26] = { 0 };
// Initialize the returned char** array and its length
*returnSize = 0;
char** ret = (char**)malloc(sizeof(char*) * 100);
// Return NULL if input array length is 0
if(!wordsSize)
return NULL;
int i;
// Update the letter frequency of the first word
for(i = 0; i < strlen(words[0]); i++)
hashTable[words[0][i] - 'a']++;
// Update the letter frequency of words from the second word
for(i = 1; i < wordsSize; i++) {
// Create a new hash table to record a new word's letter frequency
int newHashTable[26] = { 0 };
int j;
for(j = 0; j < strlen(words[i]); j++) {
newHashTable[words[i][j] - 'a']++;
}
// Update the original hash table
updateHashTable(hashTable, newHashTable);
}
// Convert the character counts in the hash table to strings and put them into ret
for(i = 0; i < 26; i++) {
if(hashTable[i]) {
int j;
for(j = 0; j < hashTable[i]; j++) {
char* tempString = (char*)malloc(sizeof(char) * 2);
tempString[0] = i + 'a';
tempString[1] = '\0';
ret[(*returnSize)++] = tempString;
}
}
}
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Scala:
object Solution {
def commonChars(words: Array[String]): List[String] = {
// Declare the immutable List collection for the result, and declare 'res' as var since it will be reassigned
var res = List[String]()
var hash = new Array[Int](26) // Count the minimal frequency of characters across all strings
// Count the occurrence of each character in the first word
for (i <- 0 until words(0).length) {
hash(words(0)(i) - 'a') += 1
}
// Count the occurrence of characters in the other words
for (i <- 1 until words.length) {
// Count the frequency in each of the remaining words
var hashOtherStr = new Array[Int](26)
for (j <- 0 until words(i).length) {
hashOtherStr(words(i)(j) - 'a') += 1
}
// Update hash to ensure it contains the minimal frequency across words
for (k <- 0 until 26) {
hash(k) = math.min(hash(k), hashOtherStr(k))
}
}
// Convert the character count in hash to the output format
for (i <- 0 until 26) {
for (j <- 0 until hash(i)) {
res = res :+ (i + 'a').toChar.toString
}
}
res
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Rust:
impl Solution {
pub fn common_chars(words: Vec<String>) -> Vec<String> {
if words.is_empty() {
return vec![];
}
let mut res = vec![];
let mut hash = vec![0; 26];
for i in words[0].bytes() {
hash[(i - b'a') as usize] += 1;
}
for i in words.iter().skip(1) {
let mut other_hash_str = vec![0; 26];
for j in i.bytes() {
other_hash_str[(j - b'a') as usize] += 1;
}
for k in 0..26 {
hash[k] = hash[k].min(other_hash_str[k]);
}
}
for (i, v) in hash.iter_mut().enumerate() {
while *v > 0 {
res.push(((i as u8 + b'a') as char).to_string());
*v -= 1;
}
}
res
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Ruby:
def common_chars(words)
result = []
# Count the minimal frequency of characters across all strings
hash = {}
# Initialize flag
is_first = true
words.each do |word|
# Record common characters
chars = []
word.split('').each do |chr|
# Initialize the first string
if is_first
chars << chr
else
# Minimal previous occurrence of the letter
if hash[chr] != nil && hash[chr] > 0
hash[chr] -= 1
chars << chr
end
end
end
is_first = false
# Clear the hash, update the minimal frequency of characters
hash.clear
chars.each do |chr|
if hash[chr] != nil
hash[chr] += 1
else
hash[chr] = 1
end
end
end
# Convert the hash of the minimal frequency into a character array
hash.keys.each do |key|
for i in 0..hash[key] - 1
result << key
end
end
return result
end
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44