-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtemplate.py
More file actions
192 lines (183 loc) · 6.58 KB
/
Copy pathtemplate.py
File metadata and controls
192 lines (183 loc) · 6.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env python3
class SuffixArray:
def __init__(self, s): # 下方的i都从1开始,字典序
if type(s) is str: # 表示排名第i小的后缀在原字符串中的起始位置为sa[i]
self.sa = self.suffix_array(s)
else:
self.sa = self.suffix_array(s[:])
self.rk = self._rk(self.sa) # 字符串中的每个位置i,其对应的后缀在后缀数组中的排名为rk[i],排名从0开始
self.height = self._height(s) # 第i小的后缀与它前一名的后缀的最长公共前缀,其他地方也可能交lcp
self.height[0] = 0 # 在字符串为单一字符构成时(长度为1也算),heigh[0]会出错成1,按照定义应该为0
def sa_naive(self, s): # 实现了最朴素的后缀数组构建算法,其时间复杂度为 O(n^2 log n),适用于小规模字符串。
n = len(s)
sa = list(range(n))
sa.sort(key=lambda x: s[x:])
return sa
def sa_doubling(self, s): # 实现了倍增算法,其时间复杂度为 O(n log n),适用于中等规模字符串。
n = len(s)
sa = list(range(n))
rnk = s
t = [0] * n
k = 1
while k < n:
sa.sort(key=lambda x: (rnk[x], rnk[x + k])
if x + k < n else (rnk[x], -1))
t[sa[0]] = 0
for i in range(1, n):
t[sa[i]] = t[sa[i - 1]]
if sa[i - 1] + k < n:
x = (rnk[sa[i - 1]], rnk[sa[i - 1] + k])
else:
x = (rnk[sa[i - 1]], -1)
if sa[i] + k < n:
y = (rnk[sa[i]], rnk[sa[i] + k])
else:
y = (rnk[sa[i]], -1)
if x < y:
t[sa[i]] += 1
k *= 2
t, rnk = rnk, t
return sa
def sa_is(self, s, upper):
# 实现了 SA-IS 算法,其时间复杂度为 O(n),适用于大规模字符串。
# SA-IS 算法基于桶排思想,通过对字符串进行分类和排序,最终得到后缀数组。
# 代码中使用了类似于桶排的技巧,通过计算各种类型的后缀数量和前缀相同的子串,将问题转化为子问题,然后递归求解。
n = len(s)
if n == 0:
return []
if n == 1:
return [0]
if n == 2:
if s[0] < s[1]:
return [0, 1]
else:
return [1, 0]
if n < 10:
return self.sa_naive(s)
if n < 50:
return self.sa_doubling(s)
ls = [0] * n
for i in range(n - 2, -1, -1):
ls[i] = ls[i + 1] if s[i] == s[i + 1] else s[i] < s[i + 1]
sum_l = [0] * (upper + 1)
sum_s = [0] * (upper + 1)
for i in range(n):
if ls[i]:
sum_l[s[i] + 1] += 1
else:
sum_s[s[i]] += 1
for i in range(upper):
sum_s[i] += sum_l[i]
if i < upper:
sum_l[i + 1] += sum_s[i]
lms_map = [-1] * (n + 1)
m = 0
for i in range(1, n):
if not ls[i - 1] and ls[i]:
lms_map[i] = m
m += 1
lms = []
for i in range(1, n):
if not ls[i - 1] and ls[i]:
lms.append(i)
sa = [-1] * n
buf = sum_s.copy()
for d in lms:
if d == n:
continue
sa[buf[s[d]]] = d
buf[s[d]] += 1
buf = sum_l.copy()
sa[buf[s[n - 1]]] = n - 1
buf[s[n - 1]] += 1
for i in range(n):
v = sa[i]
if v >= 1 and not ls[v - 1]:
sa[buf[s[v - 1]]] = v - 1
buf[s[v - 1]] += 1
buf = sum_l.copy()
for i in range(n - 1, -1, -1):
v = sa[i]
if v >= 1 and ls[v - 1]:
buf[s[v - 1] + 1] -= 1
sa[buf[s[v - 1] + 1]] = v - 1
if m:
sorted_lms = []
for v in sa:
if lms_map[v] != -1:
sorted_lms.append(v)
rec_s = [0] * m
rec_upper = 0
rec_s[lms_map[sorted_lms[0]]] = 0
for i in range(1, m):
l = sorted_lms[i - 1]
r = sorted_lms[i]
end_l = lms[lms_map[l] + 1] if lms_map[l] + 1 < m else n
end_r = lms[lms_map[r] + 1] if lms_map[r] + 1 < m else n
same = True
if end_l - l != end_r - r:
same = False
else:
while l < end_l:
if s[l] != s[r]:
break
l += 1
r += 1
if l == n or s[l] != s[r]:
same = False
if not same:
rec_upper += 1
rec_s[lms_map[sorted_lms[i]]] = rec_upper
rec_sa = self.sa_is(rec_s, rec_upper)
for i in range(m):
sorted_lms[i] = lms[rec_sa[i]]
sa = [-1] * n
buf = sum_s.copy()
for d in sorted_lms:
if d == n:
continue
sa[buf[s[d]]] = d
buf[s[d]] += 1
buf = sum_l.copy()
sa[buf[s[n - 1]]] = n - 1
buf[s[n - 1]] += 1
for i in range(n):
v = sa[i]
if v >= 1 and not ls[v - 1]:
sa[buf[s[v - 1]]] = v - 1
buf[s[v - 1]] += 1
buf = sum_l.copy()
for i in range(n - 1, -1, -1):
v = sa[i]
if v >= 1 and ls[v - 1]:
buf[s[v - 1] + 1] -= 1
sa[buf[s[v - 1] + 1]] = v - 1
return sa
def suffix_array(self, s, upper=255):
if type(s) is str:
s = [ord(c) for c in s]
return self.sa_is(s, upper)
def _rk(self, sa):
rk = [0 for _ in sa]
for i in range(len(sa)):
rk[sa[i]] = i
return rk
def _height(self, s):
sa, rk = self.sa, self.rk
ht = [0] * len(sa)
k = 0
for sai in range(0, len(s)):
if k:
k -= 1
while True:
ai, bi = sai + k, sa[rk[sai] - 1] + k
if not (0 <= ai < len(s) and 0 <= bi < len(s)):
break
if max(ai, bi) >= len(s):
break
elif s[ai] == s[bi]:
k += 1
else:
break
ht[rk[sai]] = k
return ht