Huom! Käytä PyPyä, CPython on liian hidas.
Suffiksitaulukko
# Kokoaa suffiksitaulukon (SA) kaksinkertaistusmenetelmällä.
# s tulee päättyä merkkiin '$'.
def laske_sa(s):
assert s[-1] == "$"
n = len(s)
# Alustava järjestys lasketaan yksittäisten merkkien perusteella
merkit = sorted(set(s))
järj_luku = [merkit.index(c) for c in s]
pituus = 1
while pituus < n:
parit = []
for i in range(n):
if i + pituus < n:
parit.append((järj_luku[i], järj_luku[i + pituus]))
else:
parit.append((järj_luku[i], 0))
järj_luku = parien_järjestys(parit)
pituus *= 2
sa = [0] * n
for i in range(n):
sa[järj_luku[i]] = i
return sa
# Laskujärjestäminen ensin parin toisen alkion, sitten ensimmäisen alkion
# mukaan. Palauttaa jokaiselle listan kohdalle järjestysluvun. Samoille
# pareille annetaan sama järjestysluku.
def parien_järjestys(parit):
n = len(parit)
määrät_0 = [0] * (n + 1)
määrät_1 = [0] * (n + 1)
for i in range(n):
määrät_0[parit[i][0]+1] += 1
määrät_1[parit[i][1]+1] += 1
for i in range(1, n+1):
määrät_0[i] += määrät_0[i-1]
määrät_1[i] += määrät_1[i-1]
järjestys_1 = [0] * n
for i in range(n):
kohta = määrät_1[parit[i][1]]
järjestys_1[kohta] = i
määrät_1[parit[i][1]] += 1
järjestys_0 = [0] * n
for i in järjestys_1:
kohta = määrät_0[parit[i][0]]
järjestys_0[kohta] = i
määrät_0[parit[i][0]] += 1
järj_luku = [0] * n
edellinen = None
laskuri = -1
for i in järjestys_0:
if parit[i] != edellinen: laskuri += 1
järj_luku[i] = laskuri
edellinen = parit[i]
return järj_luku
LCP-taulukko
# Laskee LCP-taulukon merkkijonon ja sen suffiksitaulukon perusteella.
# s tulee päättyä merkkiin '$'.
def laske_lcp(s, sa):
assert s[-1] == "$"
n = len(s)
järj_luku = [0] * n
edeltävä = [0] * n
for i in range(n):
järj_luku[sa[i]] = i
if i > 0: edeltävä[sa[i]] = sa[i-1]
lcp = [0] * n
pituus = 0
for i in range(n-1):
if pituus > 0: pituus -= 1
while s[i + pituus] == s[edeltävä[i] + pituus]:
pituus += 1
lcp[järj_luku[i]] = pituus
return lcp
Välin binäärihaku
# Palauttaa välin, jolle annettu toinen merkkijono sijoittuu
# suffiksitaulukossa. Pari (lo, hi) tarkoittaa puoliavointa väliä [lo, hi).
def sa_väli(s, sa, p):
lo = 0
hi = len(sa)
for i, merkki in enumerate(p):
lo = binäärihaku(lo, hi, lambda sa_i: s[sa[sa_i] + i] >= merkki)
hi = binäärihaku(lo, hi, lambda sa_i: s[sa[sa_i] + i] > merkki)
return (lo, hi)
# Etsii ensimmäisen kohdan, jossa ehto toteutuu.
def binäärihaku(lo, hi, ehto):
while lo < hi:
mid = (lo + hi) // 2
if ehto(mid): hi = mid
else: lo = mid + 1
return lo
