一、题目描述
给定一个长字符串a和一段字符串b。请问,如何最快判断短字符串b中的所有字符是否都包含在a中?请编写StringContain(a, b)实现此功能。为简单讲明思想,假设输入的字符串都是大写的字母。如a=“ABCD”,b=“BAD”,则答案为True;a=“ABCD”,b=“BCE”,则答案为False。
二、解法一:蛮力轮询
可以将b中的字符逐个查询是否在a中,但是该法的时间复杂度太高,为O(mn)。实现算法如下:
def StringContain(a, b):
a = list(a)
b = list(b)
count = 0
for i in range(len(a)):
for j in range(len(b)):
if(b[j] != a[i]):
count = count + 1
if (count == len(b)):
return False
return True
三、解法二:排序后在查询
如果先拍好训,再进行蛮力查询,需要的时间复杂度比解法一有所降低,为O(m+n),只需要将a和b字符串都逐步轮询一遍即可。但不要忘记了还有一个排序复杂度,用自带的函数即可,时间复杂度为:O(mlog(m)+nlog(n))。
最终的参考代码如下:
def StringContain(a, b):
a = list(a)
b = list(b)
a = sorted(a)
b = sorted(b)
pa = 0
pb = 0
while (pb < len(b)):
while((pa < len(a)) & (a[pa] < b[pb])):
pa = pa + 1
if((pa >= len(a)) | (a[pa] >b[pb])):
return False
pb = pb + 1
return True
四、解法三:素数相乘
素数相乘的思路是将素数代替字母,因为素数具有被本身和1整除的性质,因此可以利用该性质判断b中的字符是否在a中,时间复杂度为:O(m+n)。具体的解法思路如下:
- 建立一个list,将从小到大的顺序将素数编排到列表中,然后用程序将对应的字符换成素数;
- 遍历a字符串,得到a字符的素数相乘的结果f;
- 遍历b字符串,将第2步得到的结果除以b中每一个字符对应的素数,如果能整除,则b的该字符在a中。
具体代码如下:
def StringContain(a, b):
p = [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]
f = 1
for i in range(len(a)):
x = p[ord(a[i]) - ord('A')]
if (f % x): ## 同样的字符对应的素数不要重复乘,防止增加无效的数据量
f = f * x
for i in range(len(b)):
x = p[ord(b[i]) - ord('A')]
if(f % x):
return False
return True
五、解法四:位运算法
位运算法比较巧妙,学过微机原理或者计算机原理可能好理解一点,这里用到了移位的知识,通过移位后的结果来判断b中是否具有该字符。时间复杂度为:O(m+n)。
具体实现代码如下:
def StringContain(a, b):
hash = 0
for i in range(len(a)):
hash = hash | (1 << (ord(a[i])-ord('A')))
for i in range(len(b)):
if( (hash & (1 << (ord(b[i])-ord('A')))) == 0 ):
return False
return True
ord()是python中将字符换成ascii码的一个内置操作。反之为chr(),将ascii码换成字符。
作者在书中说这还不是最完美的,还有更完美的方法,欢迎读者在下方留言讨论。
六、课后题:变位词
题目:如果两个字符串中的字符一样,出现的次数也一样,只是出现的顺序不一样,则认为这两个字符串是兄弟字符串。如“bad”和“abd”,即为兄弟字符串。
为了简单说明,假设输入的都是大写字母。直接利用位运算法,可以检查一样的字符,但是检查不出字符的个数,如abc和abcc。
改进:建立一个list列表,每一个字符用一个单独的位运算法,加入到list中,最后将list中的元素相加,判断对应的十进制是否相等。但是这个还有其他情况,比如两个不一样的字符刚好相加是相等的,因此还要用位运算法去判断里面的字符是否相等。
轮寻和排序后轮寻都可以解决这个问题。第一步都是判断一字符长度是否相等,相等在继续下面操作。直接轮寻每找到一个对应的字符,双方都要删除该字符,直到最后一个字符。排序后轮寻需要双方的都加一,一起指向对应的下一个字符。
位运算法具体实现如下:
def StringContain(a, b):
hash = 0
hasha = 0
hashb = 0
for i in range(len(a)):
hash = hash | (1 << (ord(a[i]) - ord('A')))
hasha = hasha + (1 << (ord(a[i])-ord('A')))
for i in range(len(b)):
hashb = hashb + (1 << (ord(b[i])-ord('A')))
if( (hash & (1 << (ord(b[i])-ord('A')))) == 0 ):
return False
print(hasha)
print(hashb)
if(hasha == hashb):
return True
else:
return False
整个代码地址:https://github.com/idotc/Interview-And-Algorithm-Experience/tree/master/第二章