1160. Find Words That Can Be Formed by Characters
Problem Description
You are given an array of strings called words
and a string called chars
.
A string from the words
array is considered "good" if you can form it using only the characters available in chars
. Each character in chars
can only be used once per word (but the same chars
string can be used to check multiple words).
Your task is to find all the "good" strings in the words
array and return the sum of their lengths.
For example:
- If
chars = "atach"
andwords = ["cat", "bt", "hat", "tree"]
- "cat" can be formed using 'c', 'a', 't' from
chars
→ good string (length 3) - "bt" cannot be formed because there's no 'b' in
chars
→ not good - "hat" can be formed using 'h', 'a', 't' from
chars
→ good string (length 3) - "tree" cannot be formed because
chars
only has one 't' but "tree" needs two → not good - The answer would be 3 + 3 = 6
The key constraint is that each character in chars
can only be used once when checking if a single word can be formed. You need to ensure that the frequency of each character in a word doesn't exceed the frequency of that character in chars
.
Intuition
The core challenge here is determining whether we have enough characters in chars
to build each word. This is fundamentally a frequency comparison problem.
Think of it like having a bag of letter tiles (like in Scrabble). The string chars
represents all the tiles we have available. For each word, we need to check if we have enough of each required letter tile to spell that word.
The natural approach is to count how many of each character we have available in chars
, and then for each word, count how many of each character we need. If for every character in the word, we have at least as many available in chars
, then we can form that word.
For example, if chars = "aabbc"
, we have:
- 2 'a's
- 2 'b's
- 1 'c'
If we want to check the word "abc", we need 1 'a', 1 'b', and 1 'c'. Since we have enough of each, we can form it.
If we want to check the word "aaab", we need 3 'a's and 1 'b'. But we only have 2 'a's available, so we cannot form this word.
This leads us to use frequency counting (Counter in Python) - first count the available characters in chars
, then for each word, count its character requirements and compare. If all character requirements are met, add that word's length to our answer.
Solution Approach
We implement the frequency counting approach using Python's Counter
data structure.
Step 1: Count available characters
cnt = Counter(chars)
This creates a frequency map of all characters in chars
. For example, if chars = "atach"
, then cnt
would be {'a': 2, 't': 1, 'c': 1, 'h': 1}
.
Step 2: Initialize the answer
ans = 0
This will store the sum of lengths of all good strings.
Step 3: Check each word
for w in words: wc = Counter(w)
For each word w
in the words
array, we create a frequency map wc
of its characters.
Step 4: Verify if the word can be formed
if all(cnt[c] >= v for c, v in wc.items()):
ans += len(w)
This is the key validation step. We iterate through each character c
and its count v
in the word's frequency map wc
. For each character:
cnt[c]
gives us how many times characterc
appears inchars
v
tells us how many times we need characterc
for the current word- We check if
cnt[c] >= v
(do we have enough of this character?)
The all()
function returns True
only if this condition holds for every character in the word. If it does, we can form the word, so we add its length to our answer.
Step 5: Return the result
return ans
The time complexity is O(N × M)
where N
is the number of words and M
is the average length of each word, since we need to count characters for each word. The space complexity is O(1)
as the Counter objects can have at most 26 entries (for lowercase English letters).
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Given:
chars = "aabbc"
words = ["ab", "abc", "baa", "cc"]
Step 1: Count available characters in chars
cnt = Counter("aabbc") # cnt = {'a': 2, 'b': 2, 'c': 1}
We have 2 'a's, 2 'b's, and 1 'c' available.
Step 2: Initialize answer
ans = 0
Step 3: Check each word
Word 1: "ab"
wc = Counter("ab") # wc = {'a': 1, 'b': 1}
Check if we can form it:
- Need 1 'a', have 2 'a's ✓ (2 >= 1)
- Need 1 'b', have 2 'b's ✓ (2 >= 1)
- All checks pass! Add length:
ans = 0 + 2 = 2
Word 2: "abc"
wc = Counter("abc") # wc = {'a': 1, 'b': 1, 'c': 1}
Check if we can form it:
- Need 1 'a', have 2 'a's ✓ (2 >= 1)
- Need 1 'b', have 2 'b's ✓ (2 >= 1)
- Need 1 'c', have 1 'c' ✓ (1 >= 1)
- All checks pass! Add length:
ans = 2 + 3 = 5
Word 3: "baa"
wc = Counter("baa") # wc = {'b': 1, 'a': 2}
Check if we can form it:
- Need 1 'b', have 2 'b's ✓ (2 >= 1)
- Need 2 'a's, have 2 'a's ✓ (2 >= 2)
- All checks pass! Add length:
ans = 5 + 3 = 8
Word 4: "cc"
wc = Counter("cc") # wc = {'c': 2}
Check if we can form it:
- Need 2 'c's, have 1 'c' ✗ (1 < 2)
- Check fails! Don't add this word's length.
Step 4: Return the result
return ans # Returns 8
The final answer is 8, which is the sum of lengths of "ab" (2) + "abc" (3) + "baa" (3).
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def countCharacters(self, words: List[str], chars: str) -> int:
6 # Count frequency of each character in the available chars string
7 char_count = Counter(chars)
8
9 # Initialize total length of valid words
10 total_length = 0
11
12 # Check each word in the words list
13 for word in words:
14 # Count frequency of each character in the current word
15 word_char_count = Counter(word)
16
17 # Check if the word can be formed using available characters
18 # All characters in the word must have sufficient count in chars
19 if all(char_count[char] >= count for char, count in word_char_count.items()):
20 # If valid, add the word's length to total
21 total_length += len(word)
22
23 return total_length
24
1class Solution {
2 public int countCharacters(String[] words, String chars) {
3 // Create frequency array for available characters
4 int[] availableCharCount = new int[26];
5
6 // Count frequency of each character in chars string
7 for (int i = 0; i < chars.length(); i++) {
8 availableCharCount[chars.charAt(i) - 'a']++;
9 }
10
11 // Initialize result to store sum of lengths of valid words
12 int totalLength = 0;
13
14 // Check each word to see if it can be formed
15 for (String word : words) {
16 // Create frequency array for current word
17 int[] wordCharCount = new int[26];
18 boolean canFormWord = true;
19
20 // Check if word can be formed using available characters
21 for (int i = 0; i < word.length(); i++) {
22 int charIndex = word.charAt(i) - 'a';
23 wordCharCount[charIndex]++;
24
25 // If current character count exceeds available count, word cannot be formed
26 if (wordCharCount[charIndex] > availableCharCount[charIndex]) {
27 canFormWord = false;
28 break;
29 }
30 }
31
32 // If word can be formed, add its length to result
33 if (canFormWord) {
34 totalLength += word.length();
35 }
36 }
37
38 return totalLength;
39 }
40}
41
1class Solution {
2public:
3 int countCharacters(vector<string>& words, string chars) {
4 // Count frequency of each character in the available chars string
5 int charFrequency[26]{};
6 for (char& c : chars) {
7 ++charFrequency[c - 'a'];
8 }
9
10 int totalLength = 0;
11
12 // Check each word to see if it can be formed
13 for (auto& word : words) {
14 // Track character frequency for current word
15 int wordCharFrequency[26]{};
16 bool canFormWord = true;
17
18 // Check if each character in word is available in chars
19 for (auto& c : word) {
20 int charIndex = c - 'a';
21 // Increment count for this character in current word
22 if (++wordCharFrequency[charIndex] > charFrequency[charIndex]) {
23 // Word cannot be formed if we need more of this character
24 // than what's available in chars
25 canFormWord = false;
26 break;
27 }
28 }
29
30 // If word can be formed, add its length to total
31 if (canFormWord) {
32 totalLength += word.size();
33 }
34 }
35
36 return totalLength;
37 }
38};
39
1/**
2 * Counts the total length of words that can be formed using characters from a given string
3 * @param words - Array of words to check
4 * @param chars - Available characters to form words
5 * @returns Total length of all valid words
6 */
7function countCharacters(words: string[], chars: string): number {
8 // Helper function to convert character to array index (0-25)
9 const getCharIndex = (character: string): number => {
10 return character.charCodeAt(0) - 'a'.charCodeAt(0);
11 };
12
13 // Count frequency of each character in the available chars
14 const availableCharCount: number[] = new Array(26).fill(0);
15 for (const character of chars) {
16 availableCharCount[getCharIndex(character)]++;
17 }
18
19 // Track total length of valid words
20 let totalLength: number = 0;
21
22 // Check each word to see if it can be formed
23 for (const word of words) {
24 // Count frequency of characters in current word
25 const wordCharCount: number[] = new Array(26).fill(0);
26 let isValidWord: boolean = true;
27
28 // Check if word can be formed with available characters
29 for (const character of word) {
30 const charIndex: number = getCharIndex(character);
31 wordCharCount[charIndex]++;
32
33 // If word needs more of this character than available, mark as invalid
34 if (wordCharCount[charIndex] > availableCharCount[charIndex]) {
35 isValidWord = false;
36 break;
37 }
38 }
39
40 // Add word length to total if it can be formed
41 if (isValidWord) {
42 totalLength += word.length;
43 }
44 }
45
46 return totalLength;
47}
48
Time and Space Complexity
The time complexity is O(L)
, where L
is the sum of the lengths of all strings in the problem (including the length of chars
and the total length of all words in words
). Breaking this down:
- Creating
Counter(chars)
takesO(n)
wheren
is the length ofchars
- For each word
w
inwords
, creatingCounter(w)
takesO(m)
wherem
is the length of that word - Checking
all(cnt[c] >= v for c, v in wc.items())
iterates through at mostm
unique characters in wordw
- The total operations across all words is proportional to the sum of all string lengths
The space complexity is O(C)
, where C
is the size of the character set. In this problem, C = 26
(lowercase English letters). This is because:
Counter(chars)
stores at mostC
unique characters- Each
Counter(w)
also stores at mostC
unique characters - Since we only maintain one word counter at a time (not storing all of them), the space used is bounded by
O(C)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Reusing Characters Across Multiple Checks
A common mistake is thinking that characters from chars
get "consumed" when checking words. Each word is checked independently against the full chars
string.
Incorrect thinking:
# WRONG: Modifying the character count
char_count = Counter(chars)
for word in words:
word_char_count = Counter(word)
if all(char_count[char] >= count for char, count in word_char_count.items()):
total_length += len(word)
# DON'T DO THIS - subtracting used characters
for char, count in word_char_count.items():
char_count[char] -= count
Correct approach:
Each word gets checked against the original chars
independently. The same char_count
is reused for all words without modification.
Pitfall 2: Not Handling Missing Characters Properly
When a character in the word doesn't exist in chars
, Counter
returns 0 by default, which makes the comparison work correctly. However, using a regular dictionary would cause a KeyError.
Problematic code:
# Using regular dict instead of Counter
char_count = {}
for c in chars:
char_count[c] = char_count.get(c, 0) + 1
for word in words:
word_char_count = {}
for c in word:
word_char_count[c] = word_char_count.get(c, 0) + 1
# This would fail if word contains a character not in chars
if all(char_count[char] >= count for char, count in word_char_count.items()):
# KeyError if char not in char_count
Solution:
Either use Counter
(which returns 0 for missing keys) or handle missing keys explicitly:
if all(char_count.get(char, 0) >= count for char, count in word_char_count.items()):
Pitfall 3: Character Frequency vs Character Presence
Simply checking if all characters exist in chars
is insufficient. You must verify that the frequency requirements are met.
Wrong approach:
# INCORRECT: Only checks presence, not frequency
chars_set = set(chars)
for word in words:
if all(c in chars_set for c in word):
total_length += len(word)
This would incorrectly accept "tree" when chars = "ter"
because all unique characters ('t', 'r', 'e') are present, ignoring that we need two 'e's.
Correct approach: Always compare character frequencies, not just presence.
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!