1. 程式人生 > >算法之--字符串包含【python實現】

算法之--字符串包含【python實現】

!= str 一個數 算法 打印 bce 程序 bad 序號

題目描述

給定兩個分別由字母組成的字符串A和字符串B,字符串B的長度比字符串A短。請問,如何最快地判斷字符串B中所有字母是否都在字符串A裏?

為了簡單起見,我們規定輸入的字符串只包含大寫英文字母,請實現函數bool StringContains(string &A, string &B)

比如,如果是下面兩個字符串:

String 1:ABCD

String 2:BAD

答案是true,即String2裏的字母在String1裏也都有,或者說String2是String1的真子集。

如果是下面兩個字符串:

String 1:ABCD

String 2:BCE

答案是false,因為字符串String2裏的E字母不在字符串String1裏。

同時,如果string1:ABCD,string 2:AA,同樣返回true。

分析與解法

題目描述雖長,但題意很明了,就是給定一長一短的兩個字符串A,B,假設A長B短,要求判斷B是否包含在字符串A中。

初看似乎簡單,但實現起來並不輕松,且如果面試官步步緊逼,一個一個否決你能想到的方法,要你給出更好、最好的方案時,恐怕就要傷不少腦筋了。

解法一

判斷string2中的字符是否在string1中?最直觀也是最簡單的思路是,針對string2中每一個字符,逐個與string1中每個字符比較,看它是否在String1中。

代碼可如下編寫:

def StringContian(strA, strB):
    
for i in strB: # 循環字符串A m = 0 # 每次循環設置不等個數為0 for j in strA: # 循環字符串B if i != j: # 如果字符串A中的第j個字符串和B中的第j個字符串不相等,就加1,不相等就加0 m += 1 if m >= len(strA): # 如果不相等個數大於等於A字符串長度,就說明B的第i個字符在A字符串中沒有,則絕對不包含 return
False return True # 表明B中的每個字符在A字符串中都出現了,返回Ture strA = input().strip() # 獲取輸入字符串A strB = input().strip() # 獲取輸入字符串B print(StringContian(strA,strB)) # 打印結果

假設n是字符串String1的長度,m是字符串String2的長度,那麽此算法,需要O(n*m)次操作。顯然,時間開銷太大,應該找到一種更好的辦法。

解法二

如果允許排序的話,我們可以考慮下排序。比如可先對這兩個字符串的字母進行排序,然後再同時對兩個字串依次輪詢。兩個字串的排序需要(常規情況)O(m log m) + O(n log n)次操作,之後的線性掃描需要O(m+n)次操作。

關於排序方法,可采用最常用的快速排序,參考代碼如下:

def StringContian(strA, strB):
    strA = ‘‘.join(sorted(strA))    # 字符串A排序
    strB = ‘‘.join(sorted(strB))    # 字符串B排序
    for i in range(len(strB)):      # 循環字符串B
        m = 0
        while m < len(strA) and (strA[m] < strB[i]):    # 循環字符串A,如果A中某個字符小於B中的第i個字符,則加1
            m += 1
        if m >= len(strA) or strA[m] > strB[i]:         # 加完之後,如果長度大於等於A的長度,或者A中的第m+1個字符比B中第i個字符大,則說明字符串A不包含字符串B
            return False
    return True

strA = input().strip()          # 獲取輸入字符串A
strB = input().strip()          # 獲取輸入字符串B
print(StringContian(strA,strB)) # 打印結果

解法三

有沒有比快速排序更好的方法呢?

我們換一種角度思考本問題:

假設有一個僅由字母組成字串,讓每個字母與一個素數對應,從2開始,往後類推,A對應2,B對應3,C對應5,......。遍歷第一個字串,把每個字母對應素數相乘。最終會得到一個整數。

利用上面字母和素數的對應關系,對應第二個字符串中的字母,然後輪詢,用每個字母對應的素數除前面得到的整數。如果結果有余數,說明結果為false。如果整個過程中沒有余數,則說明第二個字符串是第一個的子集了(判斷是不是真子集,可以比較兩個字符串對應的素數乘積,若相等則不是真子集)。

思路總結如下:

  1. 按照從小到大的順序,用26個素數分別與字符‘A‘到‘Z‘一一對應。
  2. 遍歷長字符串,求得每個字符對應素數的乘積。
  3. 遍歷短字符串,判斷乘積能否被短字符串中的字符對應的素數整除。
  4. 輸出結果。

如前所述,算法的時間復雜度為O(m+n)的最好的情況為O(n)(遍歷短的字符串的第一個數,與長字符串素數的乘積相除,即出現余數,便可退出程序,返回false),n為長字串的長度,空間復雜度為O(1)。

# 此方法只有理論意義,因為整數乘積很大,有溢出風險
def StringContian(strA, strB):
    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] # 將26個字母對應26個素數
    f = 1
    for i in strA:                  # 循環字符串A
        x = p[ord(i) - ord(A)]    # 求出第i個字符對應的素數
        if (f % x):                 # 如果字符串未重復,則給f乘以對應的素數
            f *= x
    for j in strB:                  # 循環字符串B
        y = p[ord(j) - ord(A)]    # 如果第j個字符在字符串A中出現,則余數為0,否則余數不為0,返回False
        if (f % y):
            return False
    return True

strA = input().strip()          # 獲取輸入字符串A
strB = input().strip()          # 獲取輸入字符串B
print(StringContian(strA,strB)) # 打印結果

此種素數相乘的方法看似完美,但缺點是素數相乘的結果容易導致整數溢出。

解法四

如果面試官繼續追問,還有沒有更好的辦法呢?計數排序?除了計數排序呢?

事實上,可以先把長字符串a中的所有字符都放入一個Hashtable裏(python可理解為字典或者列表),然後輪詢短字符串b,看短字符串b的每個字符是否都在Hashtable裏,如果都存在,說明長字符串a包含短字符串b,否則,說明不包含。

def StringContian(strA, strB):
    for i in strB:          # 循環遍歷字符串B中每個字符
        if i not in strA:   # 如果第i個字符沒有在A中出現,則返回False
            return False
    return True

strA = input().strip()          # 獲取輸入字符串A
strB = input().strip()          # 獲取輸入字符串B
print(StringContian(strA,strB)) # 打印結果

這個方法的實質是用字符串本身代替了hashtable,空間復雜度為O(1),時間復雜度還是O(n)。

舉一反三

  • 一個字段中存儲了若幹個單詞
  • 查找指定單詞在字典中的兄弟單詞個數
  • 將找到的兄弟按字典順序排序,打印出指定的某個序號的單詞(從1開始)
  • 輸入描述:先輸入單詞個數n,再輸入n個單詞作為字典,然後再輸入一個單詞,查找其在字典中的兄弟單詞個數,再輸入要打印的兄弟單詞中的第m個

輸入:3 abc bca cab abc 1

輸出:2 bca

while True:
    try:
        s = input().strip().split()
        dict_num = int(s[0])
        bro_search_num = int(s[-1])
        dict_list = []
        for i in range(1,dict_num+1):
            dict_list.append(s[i])
        key = s[dict_num + 1]
        result = []
        for j in dict_list:
            if len(j) != len(key) or j == key:
                continue
            letter = list(j)
            for m in key:
                if m in letter:
                    letter.remove(m)
            if len(letter) == 0:
                result.append(j)
        result.sort()
        print(len(result))
        if bro_search_num <= len(result):
            print(result[bro_search_num - 1])
    except:
        break

算法之--字符串包含【python實現】