輸入:第一行是一個數字N,表示N個case,之後每個case第一行是一個數字M,表示這個case有M個數字輸入,接下來是M個數字,每個數字範圍是0<=n<K
輸出:對每個case,輸出M個數字去重後的數量
當然,這題本身沒什麼難度,弄個hash_set就行了,不過爲了說明問題,我們假定做題的人比較笨,使用bitmap:
bm = BitMap(K)
N = read_int()
for i from 1 to N:
M = read_int()
for j from 1 to M:
bm.set(read_int())
count = 0
for k from 0 to K-1:
if bm.is_set(k):
count += 1
bm.unset(k)
print count
構造一個bitmap,對於每個case,設置對應的位,最後統計數量,這裏借鑑了上一篇中的做法,統計的時候順便unset,就省去了初始化,空間和時間複雜度都是O(K),指數級的,所以說這是一個比較笨的算法(爲何是指數級,後面再說)
換一種思路,對於每個case的M個數字,如果每次read_int後判斷下是否已在位圖,則可以實時統計count,修改j的循環爲:
bm.reset()
count = 0
for j from 1 to M:
num = read_int()
if bm.is_set(num):
continue
count += 1
bm.set(num)
不過這並沒什麼改善,雖然後面k的循環省了,bm.reset()依然要遍歷
由於bitmap中每個元素只有0和1兩種狀態,每個case是獨立的,因此每次要初始化(上述兩種方案只是初始化方式不同),如果將狀態擴展爲多個,就能避免反覆初始化:
tag = new int[K]; //假設自動初始化爲全0
N = read_int()
for i from 1 to N:
M = read_int()
count = 0
for j from 1 to M:
num = read_int()
if tag[num] == i:
continue
count += 1
tag[num] = i
print count
用一個整數數組tag代替bitmap,初始化爲0,對於第i個case,用i來標記num,這樣一來每個case雖然公用tag,但各自標記方法不同,就不用初始化了,不考慮tag的建立,就單個case來說,時間複雜度是O(M)
於是,對於上篇末尾說的延遲清除,如果將marked和unmarked兩種狀態標記做一下改動,就可以區分已標記、待回收和新對象,而且省卻了全局初始化,代價就是垃圾收集器自己維護一個遞增的版本管理。不過,實際使用的算法不一定是這麼寫的