1
0
mirror of https://github.com/apachecn/lmpythw-zh.git synced 2025-05-28 12:02:19 +00:00
lmpythw-zh/ex22.md
wizardforcel a61ac21a73 ex22
2017-08-09 21:10:21 +08:00

67 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 练习 22后缀数组
> 原文:[Exercise 22: Suffix Arrays](https://learncodethehardway.org/more-python-book/ex22.html)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
我想告诉你一个关于后缀数组的故事。在一段时间里,我正在西雅图的一家公司面试,当时好奇的是如何最有效地创建一个用于可执行二进制文件的`diff`。我的研究给我带来了后缀数组和后缀树。后缀数组只是,将字符串的所有后缀排序,储存到有序列表中。后缀树是类似的,但是比列表更像`BSTree`。这些算法相当简单,一旦你进行了排序操作,它们就具有很快的性能。他们解决的问题是,找到两个字符串之间最长的公共子串(或者在这种情况下是字节列表)。
你可以在 Python 中轻易创建一个后缀数组:
```py
>>> magic = "abracadabra"
>>> magic_sa = []
>>> for i in range(0, len(magic)):
... magic_sa.append(magic[i:])
...
>>> magic_sa
['abracadabra', 'bracadabra', 'racadabra', 'acadabra',
'cadabra', 'adabra', 'dabra', 'abra', 'bra', 'ra', 'a']
>>> magic_sa = sorted(magic_sa)
>>> magic_sa
['a', 'abra', 'abracadabra', 'acadabra', 'adabra', 'bra',
'bracadabra', 'cadabra', 'dabra', 'ra', 'racadabra']
>>>
```
正如你所看到的,我只是按顺序取下字符串的后缀,然后对列表进行排序。但是,这对我有什么用呢?一旦我有了这个列表,那么我可以通过这个列表的二分搜索,来找到我想要的任何后缀。这个例子很简陋,但是在实际的代码中,你可以很快地做到它,你可以跟踪所有的原始索引,所以你可以引用后缀的原始位置。它与其他搜索算法相比非常快,对于 DNA 分析等事情非常有用。
回到西雅图的面试。我在这个寒冷的房间被 C++ 程序员面试,为了一份 Java 工作。你可以断定,这不是一个非常有趣的面试,我绝对不会认为我会得到这份工作。在多年的时间中,我没有写过任何 C++,而且这个工作是针对 Java 的,当时我是一个 Java 专家。下一个面试官来了,他问我:“如何在字符串中寻找子串?”
太棒了!我在空闲时间里一直在研究这个问题。我当然知道!我跳起来走到白板,向那个家伙解释如何制作一个后缀树,它如何提高搜索性能,修改后的堆排序如何更快,后缀树的工作原理,为什么它比三叉搜索树更好,以及如何在 C 中实现。我想,如果我可以展示如何在 C 中写出来,那么这将证明,我不只是一个核心能力的 Java 码工。
那个家伙很震惊,就像我在采访室里打开一袋新鲜的榴莲一样。他看着董事会,并且有些结巴,“呃,我是在寻找一些有关 Boyer-Moore 搜索算法的东西吗?你知道吗?我愁眉苦脸地说:“是啊,就像 10 年前一样。” 他摇摇头,拿着他的东西,起身说:“好吧,我会让大家知道我的想法。”
几分钟后,下一个面试官来了。他抬头看着白板,笑了起来并嘲笑我,然后问我另一个 C++ 模板元编程问题,我无法回答。我没有得到这份工作。
## 挑战练习
在这个练习中,你将会使用我的 Python 小会话并创建自己的后缀数组搜索类。该类将使用一个字符串,将其拆成后缀列表,然后对其进行以下操作:
> `find_shortest`
> 找到以它开始的最短子串。在上面的例子中,如果我搜索`abra`,那么它应该返回`abra`,而不是`abracadabra`。
> `find_longest`
> 找到以它开始的最长子串。如果我搜索`abra`,你返回`abracadabra`。
> `find_all`
> 查找以它开始的所有子串。这意味着`abra`返回`abra`和`abracadabra`。
你将需要对此进行良好的自动测试,并进行一些性能测量。我们将在以后的练习中使用它们。完成之后,你需要进行研究性学习来完成这个练习。
## 研究性学习
+ 一旦你的测试正常工作,使用你的`BSTree`重写它,进行后缀排序和搜索。你还可以使用每个`BSTreeNode``value`,来跟踪原始字符串中存在该子串的位置。然后,你可以保留原始字符串。
+ `BStree`如何为不同搜索操作更改你的代码?是否使其更简单或更难?
## 深入学习
彻底研究后缀数组及其应用。它们非常有用,但不是被大多数程序员熟知。