【每日算法】统计「词频相同」字符的几种方式(打表技巧)|Python 主题月

简介: 【每日算法】统计「词频相同」字符的几种方式(打表技巧)|Python 主题月

网络异常,图片无法展示
|


题目描述



这是 LeetCode 上的 面试题 10.02. 变位词组 ,难度为 中等


Tag : 「哈希表」、「排序」、「计数」、「数学」、「打表」


编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。


注意:本题相对原题稍作修改


示例:


输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]
复制代码


说明:


  • 所有输入均为小写字母。
  • 不考虑答案输出的顺序。


模拟 + 排序



一个朴素的想法是根据题意进行模拟,对每个字符串进行排序作为 key,从而实现相同的「变位词」对应同一个 key,使用哈希表进行统计即可。


Java 代码:


class Solution {
    public List<List<String>> groupAnagrams(String[] ss) {
        List<List<String>> ans = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        for (String s : ss) {
            char[] cs = s.toCharArray();
            Arrays.sort(cs);
            String key = String.valueOf(cs);
            List<String> list = map.getOrDefault(key, new ArrayList<>());
            list.add(s);
            map.put(key, list);
        }
        for (String key : map.keySet()) ans.add(map.get(key));
        return ans;
    }
}
复制代码


Python 3 代码:


class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        ans = []
        hashmap = defaultdict(list)
        for s in strs:
            key = "".join(sorted(s))
            hashmap[key].append(s)
        for val in hashmap.values():
            ans.append(val)
        return ans
复制代码


  • 时间复杂度:O(\sum_{i = 0}^{n - 1}ss[i].length * \log{ss[i].length})O(i=0n1ss[i].lengthlogss[i].length)
  • 空间复杂度:O(\sum_{i = 0}^{n - 1}ss[i])O(i=0n1ss[i])


模拟 + 计数



方法一无法做到线性,主要是存在对字符串进行排序的环节。


事实上,我们可以利用字符集大小有限作为切入点(只包含小写字母),使用一个大小为 2626 的数组进行计数,然后对计数后的数组统计值进行拼接,作为哈希表的 key,从而实现线性复杂度。


Java 代码:


class Solution {
    public List<List<String>> groupAnagrams(String[] ss) {
        List<List<String>> ans = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        for (String s : ss) {
            int[] cnts = new int[26];
            for (char c : s.toCharArray()) cnts[c - 'a']++;
            StringBuilder sb = new StringBuilder();
            for (int i : cnts) sb.append(i + "_");
            String key = sb.toString();
            List<String> list = map.getOrDefault(key, new ArrayList<>());
            list.add(s);
            map.put(key, list);
        }
        for (String key : map.keySet()) ans.add(map.get(key));
        return ans;
    }
}
复制代码


Python 3 代码:


class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        ans = []
        hashmap = defaultdict(list)
        for s in strs:
            cnts = [0] * 26
            for c in s:
                cnts[ord(c) - ord('a')] += 1
            key = "_".join(map(str, cnts))
            hashmap[key].append(s)
        for val in hashmap.values():
            ans.append(val)
        return ans
复制代码


  • 时间复杂度:令 nn 为数组大小,CC 为字符集大小,固定为 2626。整体复杂度为 O(\sum_{i = 0}^{n - 1}ss[i].length + n * C)O(i=0n1ss[i].length+nC)
  • 空间复杂度:O(\sum_{i = 0}^{n - 1}ss[i])O(i=0n1ss[i])


质数分解唯一性



事实上,我们还能使用「质数分解唯一性」性质,使用质数乘积代指某个「变位词」。

具体的,我们可以先使用 static 代码块(确保只会发生一次)打表最小的 2626 个质数(任意 2626 个都可以,使用小的,乘积溢出风险低一点),这 2626 个质数分别对应了 2626 个字母。


对于一个「变位词」而言,其对应的质数乘积必然相同。


Java 代码:


class Solution {
    static int[] nums = new int[26]; 
    static {
        for (int i = 2, idx = 0; idx != 26; i++) {
            boolean ok = true;
            for (int j = 2; j <= i / j; j++) {
                if (i % j == 0) {
                    ok = false;
                    break;
                } 
            }
            if (ok) nums[idx++] = i;
        }
    }
    public List<List<String>> groupAnagrams(String[] ss) {
        List<List<String>> ans = new ArrayList<>();
        Map<Integer, List<String>> map = new HashMap<>();
        for (String s : ss) {
            int cur = 1;
            for (char c : s.toCharArray()) {
                cur *= nums[c - 'a'];
            }
            List<String> list = map.getOrDefault(cur, new ArrayList<>());
            list.add(s);
            map.put(cur, list);
        }
        for (int key : map.keySet()) ans.add(map.get(key));
        return ans;
    }
}
复制代码


Python 3 代码:


class Solution:
    nums = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        ans = []
        hashmap = defaultdict(list)
        for s in strs:
            cur = 1
            for c in s:
                cur *= self.nums[ord(c) - 97]
            hashmap[cur].append(s)
        return list(hashmap.values())
复制代码


  • 时间复杂度:O(\sum_{i = 0}^{n - 1}ss[i].length)O(i=0n1ss[i].length)
  • 空间复杂度:O(\sum_{i = 0}^{n - 1}ss[i])O(i=0n1ss[i])


溢出说明



使用 long 仍然存在溢出风险,但使用“长度不受限制”的高精度哈希值实现是不现实的。


哈希值必须是有限值域内,才有意义。


换句话说,如果使用高精度的哈希值的话,我们是无法直接将两个哈希值进行异或判断结果是否为 00 来得出哈希值是否相同的结论,而是需要使用 O(Len)O(Len) 的复杂度来判定哈希值是否相同。


因此,针对存在的哈希冲突问题,要么是解决冲突;要是使用与「字符串哈希」类似的做法,不处理溢出(相当于模 2^{64}264),但这样会存在溢出次数不一样的值对应的哈希值相同的问题,只能说是一种期望冲突不发生的做法。


最后



这是我们「刷穿 LeetCode」系列文章的第 No.面试题 10.02 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
11月前
|
算法 前端开发 数据处理
小白学python-深入解析一位字符判定算法
小白学python-深入解析一位字符判定算法
122 0
|
3月前
|
Python
掌握Python装饰器:轻松统计函数执行时间
掌握Python装饰器:轻松统计函数执行时间
247 76
|
6月前
|
存储 监控 算法
基于 PHP 语言的滑动窗口频率统计算法在公司局域网监控电脑日志分析中的应用研究
在当代企业网络架构中,公司局域网监控电脑系统需实时处理海量终端设备产生的连接日志。每台设备平均每分钟生成 3 至 5 条网络请求记录,这对监控系统的数据处理能力提出了极高要求。传统关系型数据库在应对这种高频写入场景时,性能往往难以令人满意。故而,引入特定的内存数据结构与优化算法成为必然选择。
132 3
|
6月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
10月前
|
人工智能 Shell 开发工具
[oeasy]python0041_输出ASCII码表_英文字符编码_键盘字符_ISO_646
本文介绍了ASCII码表的生成与使用,包括英文字符、数字和符号的编码。通过Python代码遍历0到127的ASCII值,解决了找不到竖线符号的问题,并解释了ASCII码的固定映射关系及其重要性。文章还介绍了ASCII码的历史背景,以及它如何成为国际标准ISO 646。最后,通过安装`ascii`程序展示了完整的ASCII码表。
140 1
|
11月前
|
数据可视化 数据挖掘 Python
Seaborn 库创建吸引人的统计图表
【10月更文挑战第11天】本文介绍了如何使用 Seaborn 库创建多种统计图表,包括散点图、箱线图、直方图、线性回归图、热力图等。通过具体示例和代码,展示了 Seaborn 在数据可视化中的强大功能和灵活性,帮助读者更好地理解和应用这一工具。
|
10月前
|
JSON 算法 数据挖掘
基于图论算法有向图PageRank与无向图Louvain算法构建指令的方式方法 用于支撑qwen agent中的统计相关组件
利用图序列进行数据解读,主要包括节点序列分析、边序列分析以及结合节点和边序列的综合分析。节点序列分析涉及节点度分析(如入度、出度、度中心性)、节点属性分析(如品牌、价格等属性的分布与聚类)、节点标签分析(如不同标签的分布及标签间的关联)。边序列分析则关注边的权重分析(如关联强度)、边的类型分析(如管理、协作等关系)及路径分析(如最短路径计算)。结合节点和边序列的分析,如子图挖掘和图的动态分析,可以帮助深入理解图的结构和功能。例如,通过子图挖掘可以发现具有特定结构的子图,而图的动态分析则能揭示图随时间的变化趋势。这些分析方法结合使用,能够从多个角度全面解读图谱数据,为决策提供有力支持。
394 0
|
11月前
|
JSON 数据格式 Python
Python实用记录(十四):python统计某个单词在TXT/JSON文件中出现的次数
这篇文章介绍了一个Python脚本,用于统计TXT或JSON文件中特定单词的出现次数。它包含两个函数,分别处理文本和JSON文件,并通过命令行参数接收文件路径、目标单词和文件格式。文章还提供了代码逻辑的解释和示例用法。
240 0
Python实用记录(十四):python统计某个单词在TXT/JSON文件中出现的次数
|
10月前
|
人工智能 开发工具 Python
[oeasy]python040_缩进几个字符好_输出所有键盘字符_循环遍历_indent
本文探讨了Python代码中的缩进问题。通过研究`range`函数和`for`循环,发现缩进对于代码块的执行至关重要。如果缩进不正确,程序会抛出`IndentationError`。文章还介绍了Python的PEP8规范,推荐使用4个空格进行缩进,并通过示例展示了如何使用Tab键实现标准缩进。最后,通过修改代码,输出了从0到122的字符及其对应的ASCII码值,但未能找到竖线符号(`|`)。文章在总结中提到,下次将继续探讨竖线符号的位置。
120 0
|
11月前
|
数据可视化 Serverless Python
Python小事例—质地不均匀的硬币的概率统计
Python小事例—质地不均匀的硬币的概率统计
200 0

热门文章

最新文章

推荐镜像

更多