假設有一大羣人排隊等待觀看棒球比賽。他們都是主場球迷,每一個人都戴着隊帽,但不是全部人都用同一種戴法,有些人正着戴,有些人反着戴。python
假定你是保安,只有在全組球迷帽子戴法一致時才能讓他們進入球場,要麼所有正着戴,要麼所有反着戴。由於每一個人對正戴和反戴的定義並不相同。所以你不能對他們說把帽子正着戴或反着戴,只能告訴他們轉一下帽子。算法
舉個栗子(咱們用 F 表示正戴,B 表示反戴)app
F F B B B F B B B F F B F工具
上面是一個13人的隊伍,位置從0 ~ 12。你能夠發出如下指令:優化
請 0 號位置的人轉一下帽子,編碼
請 1 號位置的人轉一下帽子,code
......orm
而後分別對5,9,10,12號位置的人發出一樣的指令,總共要發出六次指令。字符串
咱們也能夠發出這樣的指令:string
請 2~4 號位置的人轉一下帽子,
請 6~8號位置的人轉一下帽子,
請 11 號位置的人轉一下帽子。
只須要 3 次指令。
咱們的要求是讓保安生成的命令數最少。難度更大的問題是:可否第一次沿着隊伍就得正確答案呢?
計算正戴區間和反戴區間的個數, 區間數更少的便是咱們要反轉的帽子區間。
def pleaseconform(caps): section = [] # 統計各區間的列表 start = 0 Fnum = 0 # 正戴區間數 Bnum = 0 # 反戴區間數 for i in range(1,len(caps)): if caps[start] != caps[i]: # 標誌着新區間的產生 section.append([start , i-1 ,caps[start]]) if caps[start]=='F': Fnum += 1 else: Bnum += 1 start = i section.append([start,len(caps)-1,caps[start]]) # 6~13行代碼未添加最後一個區間,這行代碼用於添加最後一個區間 if caps[start]=='F': Fnum += 1 else: Bnum +=1 if Fnum>Bnum : flag = 'B' else: flag = 'F' for t in section: if t[2]==flag: if(t[0] == t[1]): print("請"+str(t[0])+"號位置的人反轉帽子") else: print("請"+str(t[0])+"到"+str(t[1])+"的人反轉帽子") caps = ['F','F','B','B','B','F','B','B','B','F','F','B','F'] pleaseconform(caps) """ Output: 請2到4的人反轉帽子 請6到8的人反轉帽子 請11號位置的人反轉帽子 """
# 代碼優化 def pleaseconform(caps): caps.append('M') section = [] start = 0 Fnum = 0 Bnum = 0 for i in range(1,len(caps)): if caps[start] != caps[i]: section.append([start , i-1 ,caps[start]]) if caps[start]=='F': Fnum += 1 else: Bnum += 1 start = i if Fnum>Bnum : flag = 'B' else: flag = 'F' for t in section: if t[2]==flag: if(t[0] == t[1]): print("請"+str(t[0])+"號位置的人反轉帽子") else: print("請"+str(t[0])+"到"+str(t[1])+"的人反轉帽子") caps = ['F','F','B','B','B','F','B','B','B','F','F','B','F'] pleaseconform(caps) """ Output: 請2到4的人反轉帽子 請6到8的人反轉帽子 請11號位置的人反轉帽子 """
經過觀察,實際上咱們只須要經過 caps 列表中第一隻帽子的方向,就能夠得出咱們須要反轉的是正戴區間仍是反戴區間。由於第一隻帽子方向區間的個數必定大於等於另外一方向的區間數。基於這一觀察,可以實現一個one pass 算法。
# one pass def pleaseconformonepass(caps): caps.append(caps[0]) for i in range(1,len(caps)): if(caps[i] != caps[i-1]): if(caps[i] != caps[0]): print("請"+str(i)+"號位置到") else: print(str(i-1)+"號位置的人反轉帽子") pleaseconformonepass(caps) """ Output: 請2號位置到 4號位置的人反轉帽子 請6號位置到 8號位置的人反轉帽子 請11號位置到 11號位置的人反轉帽子 """
這道謎題背後的出發點是數據壓縮。向同一方向的人發出的命令信息是相同的,能夠被壓縮爲一組較少的命令,其中每一條命令指揮一組連續的人。
數據壓縮有多種實現方式,在思路上接近於這道習題的一種算法叫作遊程編碼。舉一個最簡單的例子最容易描述,假設有如下字符串:
WWWWWWWWWWWWWBBWWWWWWWWWWWWBBBBB
使用遊程編碼算法,咱們能夠把上述字符串壓縮爲一個由數字和字符構成的字符串:
13W2B12W5B
遊程解碼算法就是把' 13W2B12W5B '解壓爲原始字符串的過程。
現代計算機的壓縮工具,即是利用了這種思想相關的算法。
如下是遊程編碼解碼的具體實現:
def youcengbianma(string): start=0 newstring = '' for i in range(1,len(string)): if(string[start]!=string[i]): newstring += str(i-start) newstring += string[start] start=i newstring += str(i-start+1) newstring += string[start] return newstring print(youcengbianma("wwweeewwwweeffeee")) """ Output: 3w3e4w2e2f3e """ def youcengjiema(string): num = '' newstring = '' for i in range(0,len(string)): if(not string[i].isalpha()): # isalpha() 若是是字母字符,返回true num += string[i] else: for j in range(0,int(num)): newstring += string[i] num = '' return newstring print(youcengjiema("3w3e4w2e2f3e") """ Output: wwweeewwwweeffeee """