Before attempting this problem, you should be comfortable with:
We need to find the minimum number of stickers to spell the target string, where each sticker can be used multiple times. This is a classic memoization problem where we track which characters from the target still need to be covered. At each step, we pick a sticker that can help (contains the first remaining character) and recursively solve for the remaining characters.
1 for using current sticker plus the minimum for remaining).-1 if no valid solution exists.class Solution:
def minStickers(self, stickers: List[str], target: str) -> int:
stickCount = []
for s in stickers:
stickCount.append({})
for c in s:
stickCount[-1][c] = 1 + stickCount[-1].get(c, 0)
dp = {}
def dfs(t, stick):
if t in dp:
return dp[t]
res = 1 if stick else 0
remainT = ""
for c in t:
if c in stick and stick[c] > 0:
stick[c] -= 1
else:
remainT += c
if remainT:
used = float("inf")
for s in stickCount:
if remainT[0] not in s:
continue
used = min(used, dfs(remainT, s.copy()))
dp[remainT] = used
res += used
return res
res = dfs(target, {})
return res if res != float("inf") else -1Where is the length of the target string, is the number of stickers and is the average length of each sticker.
This approach improves on the previous one by sorting the target string. When we sort the target, strings with the same character composition map to the same state, reducing the number of unique states. Instead of tracking the exact order of remaining characters, we only care about which characters and how many of each are needed.
1 plus the minimum result from all valid sticker choices.class Solution:
def minStickers(self, stickers: List[str], target: str) -> int:
tmp = [c for c in target]
target = ''.join(sorted(tmp))
stickCount = []
for s in stickers:
stickCount.append(Counter(s))
dp = {}
dp[""] = 0
def dfs(t):
if t in dp:
return dp[t]
tarMp = Counter(t)
res = float("inf")
for s in stickCount:
if t[0] not in s:
continue
remainT = []
for c in tarMp:
if tarMp[c] > s[c]:
remainT.extend([c] * (tarMp[c] - s[c]))
remainT = ''.join(sorted(remainT))
res = min(res, 1 + dfs(remainT))
dp[t] = res
return res
ans = dfs(target)
return -1 if ans == float("inf") else ansWhere is the length of the target string, is the number of stickers and is the average length of each sticker.
We can represent the state as a bitmask where each bit indicates whether a character in the target has been covered. Starting from state 0 (no characters covered), we iterate through all states and for each sticker, compute which new state we can reach. This bottom-up approach systematically explores all possible ways to build the target.
2^n (where n is target length), all set to -1 except dp[0] = 0.0 to 2^n - 1.dp[t] != -1), try applying each sticker.dp[nextState] to be the minimum of its current value and dp[t] + 1.dp[2^n - 1], which represents all characters covered.class Solution:
def minStickers(self, stickers: List[str], target: str) -> int:
n = len(target)
N = 1 << n
dp = [-1] * N
dp[0] = 0
for t in range(N):
if dp[t] == -1:
continue
for s in stickers:
nextT = t
for c in s:
for i in range(n):
if target[i] == c and not ((nextT >> i) & 1):
nextT |= 1 << i
break
if dp[nextT] == -1 or dp[nextT] > dp[t] + 1:
dp[nextT] = dp[t] + 1
return dp[N - 1]Where is the length of the target string, is the number of stickers and is the average length of each sticker.
A common mistake is treating each sticker as a one-time use item. The problem explicitly states that stickers can be used infinitely many times. Your solution must allow revisiting the same sticker multiple times to cover repeated characters in the target.
Using the raw target string as the memoization key without normalization leads to redundant computation. Two different orderings of remaining characters (e.g., "abc" and "bac") represent the same problem state. Sorting the remaining string or using character frequency counts as the key significantly reduces the state space.
Trying every sticker at each step without filtering wastes time. If a sticker does not contain the first remaining character of the target, it cannot make progress toward completion. Always check that a sticker can contribute before recursing with it.
Failing to detect when the target cannot be spelled leads to incorrect results or infinite loops. Before starting, verify that every character in the target appears in at least one sticker. During recursion, properly propagate infinity values when no valid solution exists for a subproblem.
Modifying the sticker's character count map in place without creating a copy causes incorrect results across recursive branches. Always work with a fresh copy of the frequency map when simulating the use of a sticker to avoid corrupting state for sibling recursive calls.