程式設計之法面試和演算法心得-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)。具體的解法思路如下:
- 建立一個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/第二章