Expressive Words: Run-Length Encoding
[!NOTE] This module explores the core principles of Expressive Words: Run-Length Encoding, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. Problem Definition
Sometimes people repeat letters to represent extra feeling. For example:
"hello"→"heeellooo""hi"→"hiiii"
In 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.
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.
2. Interactive Analysis
Watch how the RLE algorithm matches the raw “Barcode” sequences.
Target RLE
Query RLE
3. Intuition
Imagine you are looking at a barcode where lines represent characters, and the thickness of the line represents how many times that character repeats.
"hello"becomes[h:1, e:1, l:2, o:1]"heeellooo"becomes[h:1, e:3, l:2, o:3]
The Strategy: Run-Length Encoding (RLE) Instead of comparing raw strings character by character, we first compress them into their “DNA sequences” of characters and counts. Once compressed, comparing two strings becomes a matter of applying a few simple rules per group:
- Characters must match:
char1 == char2. - Count constraints:
- You cannot shrink a group:
count1 >= count2. - If lengths differ, the target group must be extended to at least 3 characters: If
count1 != count2, thencount1 >= 3.
- You cannot shrink a group:
4. Solutions
class Solution {
public int expressiveWords(String s, String[] words) {
RLE srle = new RLE(s);
int ans = 0;
for(String w : words) {
RLE wrle = new RLE(w);
if(isStretchy(srle, wrle)) {
ans++;
}
}
return ans;
}
public boolean isStretchy(RLE srle, RLE wrle) {
if (srle.chars.size() != wrle.chars.size()) return false;
for (int i = 0 ; i < srle.chars.size() ; i++) {
char ch1 = srle.chars.get(i);
char ch2 = wrle.chars.get(i);
if (ch1 != ch2) return false;
int c1 = srle.counts.get(i);
int c2 = wrle.counts.get(i);
if (c1 < c2) return false; // Cannot shrink
if (c1 != c2 && c1 < 3) return false; // Standard limit >= 3 rule
}
return true;
}
class RLE {
List<Character> chars;
List<Integer> counts;
public RLE(String str) {
int i = 0;
List<Character> newChars = new ArrayList<>();
List<Integer> newCounts = new ArrayList<>();
while(i < str.length()) {
char c = str.charAt(i);
int j = i;
newChars.add(c);
while(j < str.length() && str.charAt(j) == c) {
j++;
}
newCounts.add(j - i); // Calculate group length
i = j;
}
this.chars = newChars;
this.counts = newCounts;
}
}
}
package main
type RLE struct {
chars []byte
counts []int
}
func getRLE(s string) RLE {
var chars []byte
var counts []int
n := len(s)
for i := 0; i < n; {
c := s[i]
j := i
for j < n && s[j] == c {
j++
}
chars = append(chars, c)
counts = append(counts, j-i)
i = j
}
return RLE{chars, counts}
}
func isStretchy(sRLE, wRLE RLE) bool {
if len(sRLE.chars) != len(wRLE.chars) {
return false
}
for i := 0; i < len(sRLE.chars); i++ {
if sRLE.chars[i] != wRLE.chars[i] {
return false
}
c1 := sRLE.counts[i]
c2 := wRLE.counts[i]
if c1 < c2 {
return false
}
if c1 != c2 && c1 < 3 {
return false
}
}
return true
}
func expressiveWords(s string, words []string) int {
sRLE := getRLE(s)
ans := 0
for _, w := range words {
if isStretchy(sRLE, getRLE(w)) {
ans++
}
}
return ans
}
5. Complexity Analysis
| Strategy | Time | Space | Notes |
|---|---|---|---|
| Run-Length Encoding | O(S + W) | O(S + W) | Linear scan. Space depends on unique character groups. |
S is the length of
s, W is the total length of all words inwords.