【力扣时间】【748】【简单】最短补全码

不是什么算法大牛,只是爱好做算法题的一般社畜。

1、来看看题目

左转力扣

今天是个简单题,所谓简单题我重拳出击,所以本以为今天可以度过一个轻松的周末的。

我对于简单题的要求是:读完题的一瞬间脑内能形成大概的思路

如果你在面试里被面到算法题时,那么第一点要注意的便是时间。面试官不可能给你太多的时间让你长篇大论,所以你必须能尽快地得出解决方案,哪怕那个方案并非完美。

2、审题

题目很长,但很容易就看出是个词频统计的题目。
总结下来大概有这么些要点:

  1. 目标字符串licensePlate包含了 数字空格,但补全词仅包含 小写字母
  2. licensePlate包含有大写字母,但词频统计会忽略大小写
  3. 同一个字母在licensePlate会出现多次,且补全词要求该字母的出现次数必须大于等于licensePlate中出现的次数
  4. 符合条件的补全词可能存在多个,此时返回最先遇到的最短的补全词

3、思路

今天的简单题并没有什么陷阱。
我们分别统计licensePlate的词频,并遍历words中提供的候选词,判断该词是否满足要求,记录并返回最先遇到的最短补全词。

思路简单明确,辣么开工!

4、撸代码

class Solution {

    public String shortestCompletingWord(String licensePlate, String[] words) {
        Map<Character, Integer> source = split(licensePlate);

        String minWord = null;
        for (String word : words) {
            Map<Character, Integer> target = split(word);

            if (!match(source, target)) {
                continue;
            }

            if (minWord == null) {
                minWord = word;
            } else {
                minWord = word.length() < minWord.length() ? word : minWord;
            }
        }

        return minWord;
    }

	//统计词频
    private Map<Character, Integer> split(String word) {
        Map<Character, Integer> map = new HashMap<>();
        for (char c : word.toCharArray()) {
            if (Character.isLetter(c)) {
                char t = Character.toLowerCase(c);
                map.put(t, map.getOrDefault(t, 0) + 1);
            }
        }
        return map;
    }

	//比较词频
    private boolean match(Map<Character, Integer> source, Map<Character, Integer> target) {
        for (Map.Entry<Character, Integer> entry : source.entrySet()) {
            if (!target.containsKey(entry.getKey())) {
                return false;
            }
            if (target.get(entry.getKey()) < entry.getValue()) {
                return false;
            }
        }
        return true;
    }
}

以上是我撸出来的原始版本。
我做题的习惯在于保留完整的思路,所以代码尽量保证了可读性,没有刻意去精简。

5、解读

由于题目简单,撸代码时的思路也很明确,所以基本上流程就是顺着思路来的。

split()方法中,我统计了各个单词的词频,以Map的形式存储。注意我在审题中提到的,licensePlate中会出现扰乱我们的数字和空格,同时也要注意大小写问题。
所以在遍历字符串中的每个字符时,使用Character.isLetter()方法来判断是当前字符是否是字母,同时用Character.toLowerCase()方法来将大写字母转换为小写。

适当地依赖高级语言提供的遍历的api和函数库吧

match()方法中,我比较了当前单词和licensePlate的词频。由于题目规定补全词必须包含licensePlate中出现的所有字母,故在比较时,我循环licensePlate的所有词频,并比较模板单词是否包含此字母,并且此字母的出现次数大于等于licensePlate中出现的次数。

最后便是主函数shortestCompletingWord(),我以split()方法获取了licensePlate的词频,并循环words中出现的所有单词,在统计其词频后,以match()方法进行比较。
如果比较通过,则以minWord记录。
由于当存在多个何时的补全词时,我们应返回最先遇到的最短的补全词,故比较minWord与目标单词长度时,只有后出现的单词长度<minWord.length()时买才更新minWord。

6、提交

在我成功地通过了题目提供的几个case后,我点击了提交,得到的结果却让我有些惊讶。

没什么可说的
我知道以我的水平很难在第一反应下做出最优解,可这个结果显然证明我连次优解也算不上。

于是我开始分析自己的代码。

7、咀嚼

由于我做题的思路普遍是空间换时间,所以很少在意内存消耗,尽量提高时间消耗。

但很明今天的题目是截然相反的。

我开始计算起时间复杂度:
在计算词频时,我对每个单词的每个字母都遍历了一遍,复杂度为O(N )
在比较时,由于licensePlate出现的字母数量不定(0 - 26),以Σ计。
所以整体的时间复杂度为O(N + M*Σ)

统计词频这一步应该都无法绕开,所以我觉得更优的解估计出在比较上。
辣么是否存在耗时为O(1) 的方法呢?或许有,但我不知道

8、他人的智慧

由于今天也是工作日,所以即使我知道问题可能存在的地方,也没有过多的时间取思考。
所以在粗略想破了脑袋后,就决定去题解区吸取一下他人的智慧了。
能力进步全靠抄

官解的思路与我大同小异。
几位上镜率很高的大牛,思路也相差不大。

直到我翻到了一个号称双百的大牛的题解

在这里插入图片描述

9、总结

大牛的题解仿佛粗壮的牛子强行捅进了我的天灵盖,从而 拓宽了我的思路

优秀的解法当然值得我们记住。
但同时也要牢记一点:时间

如果你刷题纯粹是处于兴趣,辣么另当别论。
但假如你抱有一点为了面试而准备的想法的话,就请时刻记得:

最好的题解是最你的大脑最快速简单地给出的题解。它当然必须兼具一些便于实现的特点,但同时也最好是最能让你调理清晰地得出结题步骤。

勤能补拙,共勉共勉

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值