解法
状态压缩DP,从题目条件可以知道target
的长度不超过15,所以可以用整数来表示target
的哪一位已经被贴纸满足了。
状态state1
只可能从比它小的数值state2
转化过来,因为state1
必然是由state2
的某些位从0变1转化而来的
所以DP的时候可以从小到大进行,假如遍历到state
的时候:
dp[state]
还是初始化的值,那么证明之前的状态都无法到达state
,而之后的状态显然也不会到达state
,所以state
就是一个无法到达的状态,跳过即可。dp[state]
不是初始值,那么我们遍历贴纸数组,找到加一张贴纸可以到达的状态dp[now]
,dp[now]
的值可能是它原来的值,也可能是dp[state]+1
另外,有两个剪枝增加效率的方式:
- sticker里跟target无关的字母可以删除
- 如果某个sticker有的字母在另一个sticker里都有,那么它可以删除了,因为直接使用后者能达到一样的效果
class Solution(object):
def minStickers(self, stickers, target):
"""
:type stickers: List[str]
:type target: str
:rtype: int
"""
from collections import Counter
n = len(target)
ns = (1<<n)
dp = [-1]*ns
dp[0] = 0
t_count = Counter(target)
stickers = map(lambda x:Counter(x) & t_count, stickers)
i = 0
while i<len(stickers):
if any(stickers[i]&stickers[j]==stickers[i] for j in xrange(len(stickers)) if i!=j):
stickers.pop(i)
i -= 1
i += 1
for state in xrange(ns):
if dp[state]==-1:
continue
for sticker in stickers:
now = state
for letter in sticker.elements():
for i in xrange(n):
if now & (1<<i) == 0 and target[i]==letter:
now |= (1<<i)
break
if dp[now]==-1 or dp[now]> dp[state]+1:
dp[now] = dp[state]+1
return dp[ns-1]