[Leetcode] 691. Stickers to Spell Word 解题报告

这篇博客详细介绍了如何使用动态规划解决LeetCode第691题,即找到拼出给定字符串所需的最少贴纸数量。通过分析和展示解题过程,解释了如何从无限数量的不同类型的贴纸上切割和重新排列字母来完成任务。如果无法完成任务,则返回-1。具体示例和解释进一步阐述了这一问题的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

We are given N different types of stickers. Each sticker has a lowercase English word on it.

You would like to spell out the given target string by cutting individual letters from your collection of stickers and rearranging them.

You can use each sticker more than once if you want, and you have infinite quantities of each sticker.

What is the minimum number of stickers that you need to spell out the target? If the task is impossible, return -1.

Example 1:

Input:

["with", "example", "science"], "thehat"

Output:

3

Explanation:

We can use 2 "with" stickers, and 1 "example" sticker.
After cutting and rearrange the letters of those stickers, we can form the target "thehat".
Also, this is the minimum number of stickers necessary to form the target string.

Example 2:

Input:

["notice", "possible"], "basicbasic"

Output:

-1

Explanation:

We can't form the target "basicbasic" from cutting letters from the given stickers.

Note:

  • stickers has length in the range [1, 50].
  • stickers consists of lowercase English words (without apostrophes).
  • target has length in the range [1, 15], and consists of lowercase English letters.
  • In all test cases, all words were chosen randomly from the 1000 most common US English words, and the target was chosen as a concatenation of two random words.
  • The time limit may be more challenging than usual. It is expected that a 50 sticker test case can be solved within 35ms on average.

    思路

    确实是一道比较难的题目,我看了网上的解法之后才明白。而且感觉这种类型的DP在原来没有怎么见过。
    我们定义dp[s]表示为了拼成s,所需要的sticker的最小数目。如果无法拼成,则dp[s] = -1。为了消除歧义,我们这里让s里面的字符有序排列。显然dp[""] = 0,而本题目要求的结果即为dp[target]。其中DP的递推公式为:
    dp[s] = min(1 + dp[reduced_s]) for all stickers。这里reduced_s是指采用一个特定的sticker之后,s中剩余的有序字符排列。
    这道DP题目并没有采用传统的计算并记录所有子问题结果的方式,而是只有需要的时候才计算,并且将计算结果保存下来,这样在下次如果遇到同样的子问题,我们就免除了重复计算,可以直接返回结果。我们将其称之为DP + memorization。

    代码

    class Solution {
    public:
        int minStickers(vector<string>& stickers, string target) {
            int m = stickers.size();
            vector<vector<int>> mp(m, vector<int>(26, 0));  // character counts for each sticker
            unordered_map<string, int> dp;
            for (int i = 0; i < m; ++i) {
                for (char c : stickers[i]) {
                    ++mp[i][c-'a'];
                }
            } 
            dp[""] = 0;
            return helper(dp, mp, target);
        }
    private:
        int helper(unordered_map<string, int>& dp, vector<vector<int>>& mp, string target) {
            if (dp.count(target)) {         // already calculated previously, so return directly
                return dp[target];
            }
            int ans = INT_MAX, n = mp.size();
            vector<int> tar(26, 0);
            for (char c : target) {
                ++tar[c-'a'];
            }
            for (int i = 0; i < n; ++i) {           // try every sticker
                if (mp[i][target[0] - 'a'] == 0) {  // optimization
                    continue;
                }
                string s;
                for (int j = 0; j < 26; ++j) {      // apply a sticker on every character a-z
                    if (tar[j] - mp[i][j] > 0) {
                        s += string(tar[j] - mp[i][j], 'a' + j);
                    }
                }
                int tmp = helper(dp, mp, s);
                if (tmp != -1) {
                    ans = min(ans, 1 + tmp);
                }
            }
            dp[target] = ans == INT_MAX? -1 : ans;
            return dp[target];
        }
    };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值