forked from kamyu104/LeetCode-Solutions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlongest-common-subpath.py
73 lines (66 loc) · 2.64 KB
/
longest-common-subpath.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
# Time: O(m * nlogn)
# Space: O(n)
class Solution(object):
def longestCommonSubpath(self, n, paths):
"""
:type n: int
:type paths: List[List[int]]
:rtype: int
"""
def RabinKarp(arr, x): # double hashing
hashes = tuple([reduce(lambda h,x: (h*p+x)%MOD, (arr[i] for i in xrange(x)), 0) for p in P])
powers = [pow(p, x, MOD) for p in P]
lookup = {hashes}
for i in xrange(x, len(arr)):
hashes = tuple([(hashes[j]*P[j] - arr[i-x]*powers[j] + arr[i])%MOD for j in xrange(len(P))]) # in smaller datasets, tuple from list is much faster than tuple from generator, see https://stackoverflow.com/questions/16940293/why-is-there-no-tuple-comprehension-in-python
lookup.add(hashes)
return lookup
def check(paths, x):
intersect = RabinKarp(paths[0], x)
for i in xrange(1, len(paths)):
intersect = set.intersection(intersect, RabinKarp(paths[i], x))
if not intersect:
return False
return True
MOD, P = 10**9+7, (113, 109) # MOD could be the min prime of 7-digit number (10**6+3), P could be (2, 3)
left, right = 1, min(len(p) for p in paths)
while left <= right:
mid = left + (right-left)//2
if not check(paths, mid):
right = mid-1
else:
left = mid+1
return right
# Time: O(m * nlogn)
# Space: O(n)
class Solution2(object):
def longestCommonSubpath(self, n, paths):
"""
:type n: int
:type paths: List[List[int]]
:rtype: int
"""
def RabinKarp(arr, x):
h = reduce(lambda h,x: (h*P+x)%MOD, (arr[i] for i in xrange(x)), 0)
power = pow(P, x, MOD)
lookup = {h}
for i in xrange(x, len(arr)):
h = (h*P - arr[i-x]*power + arr[i])%MOD
lookup.add(h)
return lookup
def check(paths, x):
intersect = RabinKarp(paths[0], x)
for i in xrange(1, len(paths)):
intersect = set.intersection(intersect, RabinKarp(paths[i], x))
if not intersect:
return False
return True
MOD, P = 10**11+19, max(x for p in paths for x in p)+1 # MOD is the min prime of 12-digit number
left, right = 1, min(len(p) for p in paths)
while left <= right:
mid = left + (right-left)//2
if not check(paths, mid):
right = mid-1
else:
left = mid+1
return right