编程之法面试和算法心得-1.2字符串的包含

一、题目描述

给定一个长字符串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)。具体的解法思路如下:

  1. 建立一个list,将从小到大的顺序将素数编排到列表中,然后用程序将对应的字符换成素数;
  2. 遍历a字符串,得到a字符的素数相乘的结果f;
  3. 遍历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/第二章

猜你喜欢

转载自blog.csdn.net/qq_21578849/article/details/84392818