https://leetcode.com/problems/number-of-ways-to-form-a-target-string-given-a-dictionary/
不容易想到的问题转化和dp的推导
个人解答
class Solution:
def numWays(self, words: List[str], target: str) -> int:
self.N = len(words[0])
self.frequency = {}
for ch in string.ascii_lowercase:
self.frequency[ch] = [0] * self.N
# record char frequency
for w in words:
for i, ch in enumerate(w):
self.frequency[ch][i] += 1
@functools.lru_cache(None)
def f(targetIdx, wordsIdx):
if targetIdx == len(target):
return 1
if wordsIdx == self.N:
return 0
res = 0
# not choose char at current wordsIdx
res += f(targetIdx, wordsIdx + 1)
# choose current char
ch = target[targetIdx]
if self.frequency[ch][wordsIdx] > 0:
res += self.frequency[ch][wordsIdx] * f(targetIdx + 1, wordsIdx + 1)
return res
return f(0, 0) % (10**9 + 7)
题目分析
题目主要分为两步,单独每一步看都不算太难,但是整个题目不容易想到这两步。
1. 构建对应idx的频率表
其实题目条件给出的,所有word长度相同,便有这样的暗示,即,在只统计数量的情况下,选相同idx下不同word中的相同字母,是完全等效且与前后相互独立的!
因此,可以通过这一性质,简化输入,只保留在固定长度的word中,每个位置对应的各字母出现的频率,把一个二维问题,转化为了一维问题。
2. DP 0-1背包思想
0-1背包的思想可以用在很多的DP题目中,这道题便是很典型的应用,统计数量时,分两种情况讨论:
- 选择当前位置的字符
- 不选择当前位置的字符
如此经典的思想,几乎在一维DP中都会用到
除此之外,处理下特殊情况,就可以了
其它解法
题解里给出的是Top-Down的解法,得益于python的 lru_cache
,写起来比较直观,也不用显式memorize,当然也可以采用bottom-up的方式。