All prompts are owned by LeetCode. To view the prompt, click the title link above.
Completed during Weekly Contest 405 (q4)
First completed : July 07, 2024
Last updated : July 07, 2024
Related Topics : Array, String, Dynamic Programming, Suffix Array
Acceptance Rate : 19.72 %
Did the contest live and got through Q1 & Q2 very quickly. When I saw Q3 however, I got really confused so I decided to just skip it for now. Somehow, I was able to get Q4 then when I got back to Q3, I realized what the solution was and laid out the steps, but didn't have enough time to implement it. So yeah... good contest lol. First time getting 3 questions too funny enough lol.
Did a
DP
+String Comparison
to get my solution. If this TLEed or MLEed, I would have changed to aDP
+Trie
solution to optimize theword == target[i:i+len(word)]
portion. This, however, was unnecessary to pass the contest test cases, and so I moved onto another question as the contest was still active.I do want to come back to this question and try the Aho–Corasick + DP solution.
Tried adjusting for a
DP
+Trie
solution and was successful. Overall time savings brought runtimes down from an average of12300ms
down to11700ms
, though only after additional tinkering. Next step, Aho-Corasick!From Reddit:
The correct solution involves realizing that in if sum(len(w) for w in words) = M, then then are O(sqrt(M)) unique word lengths. Couple this with string hashing and you have an O(N * sqrt(M)) algorithm. ... Consider all the unique lengths as a list of length n. A lower bound of this is [1, 2, ..., n] with a total length of n(n+1)/2. Solving n(n+1)/2 = m gives n = O(sqrt(m))
Also, look into the potential for rolling hash solutions.
class Solution:
def minimumCost(self, target: str, words: List[str], costs: List[int]) -> int:
# Generate trie
trie = {}
for cost, word in zip(costs, words) :
curr = trie
for c in word :
if c not in curr :
curr[c] = {}
curr = curr[c]
# If identical word exists, use smallest cost
curr[False] = min(curr.get(False, inf), cost)
# Dynamic Programming array
dp = [0] + [inf] * len(target)
# Iterate through each
for i in range(len(target)) :
curr = trie
offset = 0
while i + offset < len(target) and target[i + offset] in curr :
curr = curr[target[i + offset]]
offset += 1
# Word ends
if False in curr :
dp[i + offset] = min(dp[i + offset], dp[i] + curr[False])
# -1 if end was not reachable
return -1 if dp[-1] == inf else dp[-1]
class Solution:
def minimumCost(self, target: str, words: List[str], costs: List[int]) -> int:
wordToCost = Counter()
for w, c in zip(words, costs) :
# Always take the lowest cost case
if w in wordToCost and wordToCost[w] <= c :
continue
wordToCost[w] = c
# Remove duplicates from being checked
words = sorted(set(words), key=lambda x: len(x))
maxWrd = len(words) - 1
dp = [0] + [inf] * len(target)
for i in range(len(target)) :
while maxWrd >= 0 and len(words[maxWrd]) > len(target) - i :
maxWrd -= 1
if maxWrd < 0 :
break
for word in words[:maxWrd + 1] :
if word == target[i:i+len(word)] :
dp[i + len(word)] = min(dp[i + len(word)], dp[i] + wordToCost[word])
return -1 if dp[-1] == inf else dp[-1]