forked from xgouchet/AutoMergeTool
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathamtlcs.py
executable file
·124 lines (103 loc) · 3.92 KB
/
amtlcs.py
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class LCSAnalyser:
"""
A utility class able to find the LCS between three strings / arrays
"""
def __init__(self,
comparator=lambda a, b: a == b,
concatenate=lambda a, b: a + b,
boxing=lambda x: [x]):
self.comparator = comparator
self.concatenate = concatenate
self.boxing = boxing
def lcs(self, b, l, r):
"""
Returns the longuest-common-subsequence between three strings/arrays
"""
size = max(len(b), len(l), len(r))
self.__reset_lcs_cache(size)
subs = self.__lcs(b, len(b) - 1, l, len(l) - 1, r, len(r) - 1, size)
self.__reset_lcs_cache(size)
return self.__concatenate_subsequences(subs)
def __lcs(self, b, pos_b, l, pos_l, r, pos_r, size):
i = pos_b
j = pos_l
k = pos_r
if (i >= 0) and (j >= 0) and (k >= 0):
if (self.comparator(b[i], l[j]) and self.comparator(l[j], r[k])):
return self.__cached_lcs(b, i - 1, l, j - 1, r, k - 1,
size) + [Subsequence(self.boxing(b[i]), i, j, k)]
else:
tmp_b = self.__cached_lcs(b, i - 1, l, j, r, k, size)
tmp_l = self.__cached_lcs(b, i, l, j - 1, r, k, size)
tmp_r = self.__cached_lcs(b, i, l, j, r, k - 1, size)
if (len(tmp_b) >= len(tmp_l)) and (len(tmp_b) >= len(tmp_r)):
return tmp_b
elif (len(tmp_l) >= len(tmp_b)) and (len(tmp_l) >= len(tmp_r)):
return tmp_l
elif (len(tmp_r) >= len(tmp_b)) and (len(tmp_r) >= len(tmp_l)):
return tmp_r
else:
raise RuntimeException("Oops")
else:
return []
def __concatenate_subsequences(self, subs):
result = []
curr = None
last_b = last_l = last_r = -1
for sub in subs:
if (curr == None):
curr = sub
last_b = sub.pos_b
last_l = sub.pos_l
last_r = sub.pos_r
elif (sub.pos_b == last_b + 1) and (sub.pos_l == last_l + 1) and (
sub.pos_r == last_r + 1):
curr = Subsequence(
self.concatenate(curr.content, sub.content), curr.pos_b, curr.pos_l, curr.pos_r)
last_b = sub.pos_b
last_l = sub.pos_l
last_r = sub.pos_r
else:
result += [curr]
curr = sub
last_b = sub.pos_b
last_l = sub.pos_l
last_r = sub.pos_r
if curr != None:
result += [curr]
return result
def __reset_lcs_cache(self, size):
self.__lcs_cache = {}
def __cached_lcs(self, b, pos_b, l, pos_l, r, pos_r, size):
key = (((pos_b * size) + pos_l) * size) + pos_r
if key in self.__lcs_cache:
return self.__lcs_cache[key]
else:
res = self.__lcs(b, pos_b, l, pos_l, r, pos_r, size)
self.__lcs_cache[key] = res
return res
class Subsequence:
"""
Represents a subsequence in an LCS result
"""
def __init__(self, content, pos_b, pos_l, pos_r):
self.content = content
self.pos_b = pos_b
self.pos_l = pos_l
self.pos_r = pos_r
def __str__(self):
return str(self.content)
def __repr__(self):
return repr(self.content) + " @ b" + str(self.pos_b) + " l" + str(self.pos_l) + " r" + str(
self.pos_r)
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
return False
def __ne__(self, other):
return not self.__eq__(other)
if __name__ == '__main__':
print("This is just a utility module, not to be launched directly.")
sys.exit(1)