吴军《谷歌面试题:倒置英文句子》
问题是这么说的:给你一个英语的语句,比如"London bridge is falling down",把它完全倒装过来,“down falling is bridge London”,如何不使用额外的存储空间完成这个倒装过程?
通常学习计算机算法的人在解决这个问题时,首先会想到把这个句子切割成一个个单词,然后把它们存到一个数组里,数组的结构大致如下:
把这个数组顺序存入,逆序取出来就可以完成语句倒装的问题。当然,还有一个类似的办法,就是把上面的单词,一个个送入堆栈,记得堆栈的先进后出,后进先出性质,就可以利用这个数据结构完成句子的倒装。
但是,这种算法要额外地使用存储空间,因此不符合题目的要求。在面试时,我们一般会让选择了上述方法的候选人把他们的想法说完,这样至少让他们在心理上不至于感受到打击,但是接下来我们会要求他们找出不使用额外内存空间的方法。
很多人想到的是把上面句子中的单词前后对调。但这道题目的难点恰恰在于英语单词的长度不同,如果不使用额外的空间,很难把不同长度的单词对调。
学习计算机的人会想到记录下来句子一头一尾两个单词的长度,然后把长的那个单词先挪开,短的那个填进长的单词空出来的位置。
比如在上面的例子中,London这个词比较长,down这个词比较短,可以把London先挪出来,把down这个词放到London的位置中,这是放得下的。但是这样接下来的问题又来了,长的单词London无法填入短的单词down留出来的空位。
当然,有人会想,在短的单词那边再挪走一个词,具体到上面的例子中,就是挪走falling,看看能否把长的单词安置进去。在这个例子中是可以的。当然,实际情况可能会比这个复杂,有可能留出的空间还不够,比如of the 这两个单词的长度加起来也没有Chinese一个长。即便句子尾巴上两个单词的位置能够放头上的一个长的单词,但也有可能挪出的空间太多了,这样句子的头上放不下两个单词,上面的例子就陷入了后一种情况。
我讲到这里,你可能已经糊涂了,这其实就对了,因为我也没有打算让你听懂,我啰哩啰嗦地讲了这么多,无非是想说,前后对调单词这件事远比我们想象的复杂得多,以至于这个看似合理的想法在计算机中几乎无法实现。
在我考察Google候选人中,我大约向十几个候选人问了这个问题,大约一半的人(10人左右)试图采用头尾单词对调的方法,但是这10个人没有一个人能够写出实现他们想法的程序,因为他们都走进了死胡同。
上面这种方法的问题在哪里呢?其实最大的问题在于人会陷入自己固有的思维方式,或者说常人的思维。这道题目中,我们要求以单词为单元进行倒装,单词本身必须维持原状,因此大家为了满足这一点要求,不敢把单词打碎,搞乱。而用计算机解决这个问题的关键,又恰恰是要把单词先搞乱,再规整起来。这道题最好的解法其实非常简单,如果你已经想到了,恭喜你,如果还没有,不妨花一分钟听我讲一下,其实只需要捅破一层窗户纸。
第一步,先将整个句子看成是一个完整的字符串,以字母为单位头尾对调,这样上面的句子就变成了下面这样一个乱七八糟的字符串:“nwod gnillaf si egdirb nodnoL”
上面这一串字,你可能根本看不懂,但是没有关系。接下来我们再完成第二步,你就看清楚了。
第二步,把用空格分割的每一个字串以字母为单位,头尾对调。比如第一个字串是nwod,头尾对调后是down,也就是原来句子中的最后一个单词。第二个字串是gnillaf,字母头尾对调后是falling,原来句子中倒数第二个单词。这样一个个地做,直到最后一个字串里的字母对调完毕。这样就得到了下面的倒装句子:“down falling is bridge London.”
这个方法为什么能成功呢?
这个方法看起来十分简单,但是有趣的是,面试Google的人有一半做不出这道题,要知道能够到Google公司面试的,十份简历也未必能挑出一两个人,并非等闲之辈。但是,囿于平时的思维定式,特别是不敢把整个句子变成无意义的字串,很多人想不到先要把整个句子变得无意义,才能得到的后面有意义的单词。
方法一
- 创建reverse01.py
"""
倒置英文句子
"""
source = "London bridge is falling down"
words = source.split(" ")
target = ""
for i in range(len(words) - 1, -1, -1):
target = target + words[i]
if i > 0:
target = target + " "
print(source)
print(target)
- 运行程序,查看结果
方法二
- 创建reverse02.py
"""
倒置英文句子
"""
def reverse(source):
source = list(source)
length = len(source)
for i in range(length // 2):
source[i], source[length - i - 1] = source[length - i - 1], source[i]
return ''.join(source)
if __name__ == '__main__':
source = "London bridge is falling down"
print(source)
temp = reverse(source)
print(temp)
for word in temp.split(' '):
print(reverse(word), end=' ')
- 运行程序,查看结果