周日参加了一个电话面试,其中有一道面试题。
题目是这样的:
输入一个字符串,如何找到和这个字符串字符内容相同的字符串,不考虑字符顺序。比如abcc和cbac就是字符内容相同,而和abc字符内容不相同。
因为本身没有遇到需要这样算法的问题,只能临场发挥了。
1,直观的感觉是建立哈希表,哈希的键位字符串的长度,这样一次查找可以找到所有相同长度的字符串,然后再次哈希,按照ascll码求出相同ascll码的字符串,然后......思维开始混乱了,因为这样计算出的ac和bb的ascll码完全相同,这时候距离最终结果还很远。
2,考虑到很早以前做过的思考。每个字符都映射一个素数,比如a映射为2,b映射为3,c映射为5。对任何一个字符串,求其各个字符的对应素数的乘积,比如60,就是由2*2*3*5得到。因为乘积与顺序无关,而素数又可以保证每个字符不相关,只要是由aabc这四个字符组成,顺序可以打乱,就可以得到60。但是当时觉得这个算法有一些问题,如果字符很多,就需要找到一些很大的素数,而如果字符串很长,这个乘积会很大。虽然有一些优化的办法,比如,最常见的字符所对应的素数小一些,字符串对应的整数可以用字符串表示,等等。但毕竟是电话面试的,没有再做考虑。
3,说完上面的算法后,突然灵机一动,又想到了其它算法。可以把字符串做一个排序,这样,打乱顺序的相同字符组成的字符串唯一对应一个排序后的字符串。把输入字符串字符排序,就得到另一个有序的字符串,按照这个字符串查找匹配即可。多么美妙的算法啊,当时就这么想。
后来仔细想了一些,算法2要比算法3好,原因如下:
1,字符串的字符范围在256以内,每一个字符用8位二进制表示有点浪费。比如,如果字符串只包括英文字符,就只需要52个字符,找到52个素数还是很容易的,n个素数相乘得到的特征值要比n个8位二进制对应的特征值要节省空间。
2,素数可以按字符的出现频率分配,越常见的字符就分配较小的素数,这样能保证字符串的特征值都比较小。比如如果字符a最常见,可以把2给他。充分利用了字符的概率分布,也可以压缩存储空间。(如果数值超过32位整数表示范围,可以扩展)
3,乘法要比排序来的快。没有严格证明,但是乘法是O(n)时间复杂度,排序时O(nlogn)。当然用桶排序也是O(n),不过又要多了空间复杂度。
4,整数比较要比字符串比较快。
不过算法2也有一些问题,主要是整数范围受限,对于比较长的字符串,整数就不够表示了,要扩展整数,凭添复杂度。
但是算法2感觉很精致,很紧凑,乘法的操作和顺序无关两者的关系那么紧密,个人比较欣赏。
可惜是电话面试,如果是当面面试就可以尽情发挥,把算法2的各种优点都说出来,因为这个算法比较别致,一般都不会想到,相信能够让面试官惊艳一下。
如果今后有人参加面试,建议当面面试,电话面试的发挥水平很难达到当面面试的8成。