算法珠玑-变位词的四种解法

  给定一个英语词典,找出其中所有变位词集合。例如,“pots”、“stops”、和“tops”互为变位词,因为每一个单词都可以通过改变其他单词中字母的顺序得到
  为了将问题简化,我们不需要对比英文词典,判断"otsp"是不是一个合法的单词(这个显然不是),因为这没有太大的意义。因此我们将问题简化为,给定任意两个英文单词word1和word2,判断是否互为变位词。
  这是一个很简单,但是很具有代表性的问题,因为这个问题的解决办法很多,优化空间很大。

暴力破解
  • 首先,我们面对这种题目,如果没有思路,第一个解法就是暴力破解,这也是第一种方法,那么怎么暴力破解呢?答案就是全排列,找出word1组成字母的全排列,判断word2在不在该全排列里面。
# 全排列
#空间复杂度:O(n!),时间复杂度:O(n!)
word1 = 'deposit'
word2 = 'topside'
word3 = 'visited'

res = []
def full_array(word, temp=''):
    if len(word)==0:
        if word not in res:
            res.append(temp)
        return 
        
    for i in range(len(word)):
        temp += word[i]
        full_array(word[:i]+word[i+1:], temp)
        temp = temp[:-1]    
        
def heterotopic_words_full_array(word1, word2):
    if len(word1) != len(word2):
        return 'no'
    full_array(word1)
    return 'yes' if word2 in res else 'no'
遍历
  • 我们接着思考,能不能优化一下?答案也是可以的。我们可以对两个word进行遍历,只要word1的字母出现在word2中,就在word2该字母所在位置进行一次标识,比如置为0,当遍历完成,我们只需要判断word2的所有位置是不是全为0即可。
# 遍历
#空间复杂度:O(n),时间复杂度:O(n^2)
def heterotopic_words_ergodic(word1, word2):
    if len(word1) != len(word2):
        return 'no'    
    word2 = list(word2)
    for c in word1:
        for i in range(len(word2)):
            if c == word2[i]:
                word2[i] = 0
    for c in word2:
        if c != 0:
            return 'no'
    
    return 'yes'

heterotopic_words_ergodic(word1, word2)
排序
  • O(n2)的时间复杂度还是太高了,能不能再优化一下?答案也是可以的。考虑到所有的字母都是按照 ASCII 或者 Unicode 进行编码,所以在内存中,字母对应的其实是一个int型数字(可以用ord(“a”)查看对应数字编码)。也就是说我们可以对两个word进行排序,然后比较两个word是不是相等即可。
# 排序
#空间复杂度:O(n),时间复杂度:O(nlog n)
def heterotopic_words_sort(word1, word2):
    if len(word1) != len(word2):
        return 'no'
    word1 = ''.join(sorted(word1))
    word2 = ''.join(sorted(word2))
    return "yes" if word1==word2 else "no"

heterotopic_words_sort(word1, word3)
排序
  • O(nlogn)的时间复杂度还是有点高了,能不能再优化一下?答案也是可以。考虑到互为变位词的两个单词必然有同样的字母构成,并且相同字母的数量相等,所以我们考虑给每一个字母加上一个标识,每当在word1中遇到,就+1,在word2中遇到,就-1,如果最后遍历一遍之后,该标识对应的value为0,表示该字母在两个单词中的数量是一致的。也就是说,我们需要确定所有字母的value是不是都为0,就能判断两个单词是不是互为变位词。
# 哈希
#空间复杂度:O(n),时间复杂度:O(n)
from collections import defaultdict
def heterotopic_words_hash(word1, word2):
    if len(word1) != len(word2):
        return 'no'
    hash_dict = defaultdict(int)
    for i in range(len(word1)):
        hash_dict[word1[i]] += 1 
        hash_dict[word2[i]] -= 1 
    
    for key in hash_dict.keys():
        if hash_dict[key] != 0:
            return 'no'        
    return 'yes'

heterotopic_words_hash(word1, word3)

  那么时间复杂度到O(n)是不是就到头了呢?我觉得应该不是的,限制程序员的永远是思维,可能过段时间就能提出一种O(log n)的优化方法了。

第一部分 编 程 技 术 第1章 性能监视工具 3 1.1 计算素数 3 1.2 使用性能监视工具 7 1.3 一个专用的性能监视工具 8 1.4 开发性能监视工具 10 1.5 原理 11 1.6 习题 11 1.7 深入阅读 12 第2章 关联数组 13 2.1 Awk中的关联数组 13 2.2 有穷状态机模拟器 16 2.3 拓扑排序 17 2.4 原理 20 2.5 习题 21 2.6 深入阅读 22 第3章 程序员的忏悔 23 3.1 二分搜索 24 3.2 选择算法 26 3.3 子程序库 28 3.4 原理 30 3.5 习题 31 第4章 自描述数据 33 4.1 名字—值对 33 4.2 记录来历 36 4.3 排序实验 37 4.4 原理 39 4.5 习题 39 第二部分 实 用 技 巧 第5章 劈开戈尔迪之结 43 5.1 小测验 43 5.2 解答 44 5.3 提示 44 5.4 原理 47 5.5 习题 48 5.6 深入阅读 49 5.7 调试(边栏) 49 第6章 计算机科学箴言集 51 6.1 编码 52 6.2 用户界面 53 6.3 调试 53 6.4 性能 54 6.5 文档 56 6.6 软件管理 56 6.7 其他 58 6.8 原理 58 6.9 习题 58 6.10 深入阅读 60 第7章 粗略估算 61 7.1 头脑热身 61 7.2 性能的经验法则 62 7.3 Little定律 64 7.4 原理 65 7.5 习题 66 7.6 深入阅读 67 7.7 日常速算(边栏) 67 第8章 人员备忘录 69 8.1 备忘录 69 8.2 原理 71 8.3 深入阅读 71 第三部分 人性化I/O 第9章 小语言 75 9.1 Pic语言 76 9.2 视角 79 9.3 Pic预处理器 81 9.4 用来实现Pic的小语言 83 9.5 原理 87 9.6 习题 88 9.7 深入阅读 89 第10章 文档设计 91 10.1 表格 92 10.2 三条设计原则 94 10.3 插图 94 10.4 文本 96 10.5 合适的媒介 98 10.6 原理 100 10.7 习题 101 10.8 深入阅读 101 10.9 次要问题目录(边栏) 101 第11章 图形化输出 103 11.1 实例研究 103 11.2 显示结果取样 105 11.3 原理 107 11.4 习题 108 11.5 深入阅读 110 11.6 拿破仑远征莫斯科(边栏) 110 第12章 对调查的研究 113 12.1 有关民意调查的问题 113 12.2 语言 114 12.3 图片 117 12.4 原理 119 12.5 习题 120 第四部分 算 法 第13章 绝妙的取样 123 13.1 取样算法一瞥 123 13.2 Floyd算法 124 13.3 随机排列 125 13.4 原理 127 13.5 习题 127 13.6 深入阅读 128 第14章 编写数值计算程序 129 14.1 问题 129 14.2 牛顿迭代 130 14.3 良好的起点 132 14.4 代码 133 14.5 原理 135 14.6 习题 135 14.7 深入阅读 137 14.8 数值算法的力量(边栏) 137 第15章 选择 141 15.1 问题 141 15.2 程序 142 15.3 运行时间分析 145 15.4 原理 148 15.5 习题 149 15.6 深入阅读 151 附录A C和Awk语言 153 附录B 一个子程序库 157 部分习题答案 165 索引 181
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lemon_tttea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值