Expressive Words
MEDIUMDescription
Sometimes people repeat letters to represent extra feeling. For example:
"hello" -> "heeellooo""hi" -> "hiiii"
In these strings like "heeellooo", we have groups of adjacent letters that are all the same: "h", "eee", "ll", "ooo".
You are given a string s and an array of query strings words. A query word is stretchy if it can be made to be equal to s by any number of applications of the following extension operation: choose a group consisting of characters c, and add some number of characters c to the group so that the size of the group is three or more.
- For example, starting with
"hello", we could do an extension on the group"o"to get"hellooo", but we cannot get"helloo"since the group"oo"has a size less than three. Also, we could do another extension like"ll" -> "lllll"to get"helllllooo". Ifs = "helllllooo", then the query word"hello"would be stretchy because of these two extension operations:query = "hello" -> "hellooo" -> "helllllooo" = s.
Return the number of query strings that are stretchy.
Example 1:
Input: s = "heeellooo", words = ["hello", "hi", "helo"] Output: 1 Explanation: We can extend "e" and "o" in the word "hello" to get "heeellooo". We can't extend "helo" to get "heeellooo" because the group "ll" is not size 3 or more.
Example 2:
Input: s = "zzzzzyyyyy", words = ["zzyy","zy","zyy"] Output: 3
Constraints:
1 <= s.length, words.length <= 1001 <= words[i].length <= 100sandwords[i]consist of lowercase letters.
Approaches
Checkout 2 different approaches to solve Expressive Words. Click on different approaches to view the approach and algorithm in detail.
Run-Length Encoding
This approach involves pre-processing the main string s and each word in the words array into a run-length encoded (RLE) format. A run-length encoding represents a string as a list of pairs, where each pair contains a character and its consecutive count. For example, "heeellooo" becomes [('h', 1), ('e', 3), ('l', 2), ('o', 3)]. After encoding, we compare the RLE of each word with the RLE of s to check for the stretchy conditions.
Algorithm
- Define a helper class or structure, say
RLE, to store the run-length encoded representation of a string. This structure would contain the compressed character sequence (e.g., as a string) and a list of counts for each character group. - Implement a constructor or a function that takes a string and populates the
RLEstructure by iterating through the string and counting consecutive identical characters. - In the main function
expressiveWords, first create anRLEobject for the input strings. - Initialize a counter for stretchy words to zero.
- Iterate through each
wordin thewordsarray. - For each
word, create itsRLErepresentation. - Compare the
RLEof thewordwith theRLEofs:- First, check if their compressed character sequences are identical. If not, the word is not stretchy.
- If the sequences match, iterate through the counts. For each corresponding pair of groups with counts
count_w(fromword) andcount_s(froms):- The word is not stretchy if
count_w > count_s. - The word is also not stretchy if
count_s < 3andcount_wis not equal tocount_s.
- The word is not stretchy if
- If all groups satisfy these conditions, the word is stretchy.
- Increment the counter for each stretchy word found.
- Return the total count.
The core idea is to abstract away the string traversal into a structured comparison. We first create a helper function or class to convert a string into its RLE representation. For the string s, this is done only once. Then, for each word in the input array, we also generate its RLE. The check for a word being 'stretchy' is then reduced to comparing these two RLE structures.
The comparison involves two main checks:
- The compressed character sequences must be identical. This means the RLEs must have the same number of groups, and the character for each group must match in sequence.
- For each corresponding group, the counts must satisfy the stretchy condition. Let
count_sbe the count froms's RLE andcount_wfrom the word's RLE. The conditions are:count_w <= count_s, and ifcount_s < 3, thencount_wmust be exactly equal tocount_s(no stretching is allowed if the final group size is less than 3).
If a word satisfies both conditions for all its character groups, it's counted as stretchy.
import java.util.ArrayList;
import java.util.List;
class Solution {
public int expressiveWords(String s, String[] words) {
RLE rleS = new RLE(s);
int ans = 0;
for (String word : words) {
RLE rleWord = new RLE(word);
if (!rleS.key.equals(rleWord.key)) {
continue;
}
boolean stretchy = true;
for (int i = 0; i < rleS.counts.size(); ++i) {
int countS = rleS.counts.get(i);
int countWord = rleWord.counts.get(i);
if (countWord > countS || (countS < 3 && countWord != countS)) {
stretchy = false;
break;
}
}
if (stretchy) {
ans++;
}
}
return ans;
}
}
class RLE {
String key;
List<Integer> counts;
public RLE(String s) {
StringBuilder keyBuilder = new StringBuilder();
counts = new ArrayList<>();
if (s == null || s.length() == 0) {
key = "";
return;
}
int i = 0;
while (i < s.length()) {
char c = s.charAt(i);
int j = i;
while (j < s.length() && s.charAt(j) == c) {
j++;
}
keyBuilder.append(c);
counts.add(j - i);
i = j;
}
key = keyBuilder.toString();
}
}
Complexity Analysis
Pros and Cons
- The logic is cleanly separated into two phases: encoding and comparing, which can improve code readability and maintainability.
- The RLE of the main string
sis computed only once, which is efficient if the number of words is large.
- Requires extra space to store the RLE representations, which can be O(L) for a string of length L.
- The overhead of creating intermediate data structures (lists, custom objects) can make it slightly slower in practice than a direct in-place comparison, despite having the same time complexity class.
Code Solutions
Checking out 3 solutions in different languages for Expressive Words. Click on different languages to view the code.
class Solution {
public
int expressiveWords(String s, String[] words) {
int ans = 0;
for (String t : words) {
if (check(s, t)) {
++ans;
}
}
return ans;
}
private
boolean check(String s, String t) {
int m = s.length(), n = t.length();
if (n > m) {
return false;
}
int i = 0, j = 0;
while (i < m && j < n) {
if (s.charAt(i) != t.charAt(j)) {
return false;
}
int k = i;
while (k < m && s.charAt(k) == s.charAt(i)) {
++k;
}
int c1 = k - i;
i = k;
k = j;
while (k < n && t.charAt(k) == t.charAt(j)) {
++k;
}
int c2 = k - j;
j = k;
if (c1 < c2 || (c1 < 3 && c1 != c2)) {
return false;
}
}
return i == m && j == n;
}
}
Video Solution
Watch the video walkthrough for Expressive Words
Similar Questions
5 related questions you might find useful
Patterns:
Data Structures:
Companies:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.