今天群里有個朋友出了個題,是一家公司的面試題,題目如下(補充:對於ip0開頭的也是無效的,如分割后001.1.1.1這種是不可以的):
分析:這里我們舉一個最簡單的例子1.1.1.12.2.2.2。首先能想到的解決方法肯定是使用循環了,我們可以寫2個循環嵌套(有點像冒泡排序)從第0個位置截取1個,從第0個位置截取2個……直到從第n-1截取到n個為止(n為字符串總長度)。
如從第1個截取1個我們截取出來的就是“1”,第1個截取2個我們截取出來的就是“1.”,第1個截取3個我們截取出來的就是“1.1”……截取出來后我們可以通過split方法用“.”拆分,然后進行驗證每個元素是否合法,如果合法我們則繼續往后遍歷。
但是這里使用循環就有個問題了,比如我們截取出來“1.1.1.1”,我們判斷合法,那么循環下次截取的時候就是1.1.1.12”了,很顯然這個並不是我們希望的結果。我們更希望當有一個合法的ip出現后,我們從它后面的位置重新開始查找合法的ip。如后面幾次的遍歷就是"2","2.","2.2"這樣的順序找。所以這里就想到了使用遞歸的方式來控制搜索的起始點和結束點。
遞歸解決思路:寫遞歸第一件事就是確定結束條件,因為遞歸寫不好很容易死循環或者棧溢出,這里我們可以很方便的算出結束條件就是起始位置是字符串長度-1的位置,結束的位置是字符長度的位置。然后我們將參數一開始的默認值設置成開始位置0,結束位置1(因為我們要將所有的情況都走一遍)。當當前截取的內容條件不滿足,我們就將結束位置+1再進行截取,直到有滿足條件的ip出現(我們保存合法的ip以及當前起始位置和結束位置存到一個列表里)我們則修改起始位置為當前結束位置+1的位置,結束位置為當前結束位置加2的位置,或者一直到結束位置到最后一個位置,我們再將起始位置設置成當前起始位置+1,結束位置為起始位置加2,再進行遞歸,直到遞歸結束。
最后我們就可以獲得一個類似這樣的數據,上面是ip,下面是當前ip對應的位置。當然你會發現一些重復的數據,因為確實會有重復的計算出現(這里如果不需要找出全部值的話可以優化)。在這里我們搜索最終結果的時候只需要按着順序看位置是不是連續到最后的,如下面的第一個[0, 7], 第二個[7, 14]是連續到最后的,所以結果就是1.1.1.1和2.2.2.2了。
ips: ['1.1.1.1', '2.2.2.2', '1.1.1.12', '1.1.12.2', '1.12.2.2', '12.2.2.2', '2.2.2.2', '2.2.2.2']
ips_pos: [[0, 7], [7, 14], [0, 8], [2, 10], [4, 12], [6, 14], [7, 14], [7, 14]]
附上本汪寫的代碼(有更好的方法歡迎一起討論^_^)
str = "1.1.1.10050.2.1.1101.2.2.2" ips = [] # 記錄的ip ips_pos = [] # 當前ip的起始位置 def showIP(str, start, end): # 結束條件 if (start == len(str)-1 and end == len(str) ): return sp = str[start:end].split(".") if (len(sp) == 4): #是否數字都滿足所有轉換規則 flag = True # 是否所有數字都在范圍內1~255 try: for i in range(0, 4): if(sp[i].startswith("0")): flag=False break now = int(sp[i]) if (not (0 < now < 256)): flag = False break if (flag): ips.append(str[start:end]) # 保存ip ips_pos.append([start,end]) # 保存ip的位置 #遞歸計算 if(end + 1 > len(str)): showIP(str, start+1 , start +2) else: showIP(str, end , end + 1) except Exception as e: pass #遞歸計算 if (end + 1 > len(str)): showIP(str, start + 1, start + 2) else: showIP(str, start, end + 1) #獲取最后的結果 def getResult(str,ips,pos): length=len(str) #長度 result=[] #最終的結果 #之前的起始和結束為止 preStart=0 preEnd=0 #所有的起始和結束位置是否全部連續 flag=True for i, item in enumerate(ips_pos): #第一次則保存為之前的值 if(i==0): preStart=item[0] preEnd=item[1] result.append(ips[i]) continue #之前結束的值等於現在開始的值則表示連續 if(preEnd==item[0]): result.append(ips[i]) preStart=item[0] preEnd=item[1] if(item[1]==length and flag): print("最終結果:",result) return #不連續 else: flag=False #新的開始則設置為連續值 if(item[0]==0): flag=True #清空結果重置之前的值 result.clear() preStart = item[0] preEnd = item[1] result.append(ips[i]) print("無結果") showIP(str, 0, 1) getResult(str,ips,ips_pos) #運行結果: #最終結果: ['1.1.1.100', '50.2.1.1', '101.2.2.2']
當然遞歸如果直接滿足條件是不是可以直接顯示結果,不在進行遞歸查找,這里大家可以自己修改下看看哈。當然也可以將多種情況都全部顯示出來,如1.1.1.123.2.2.2可以有2種結果1.1.1.1和23.2.2.2以及1.1.1.12和3.2.2.2大家都可以通過修改上面的代碼實現哦。