-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy path15_CountDistinctSlices.py
164 lines (129 loc) · 5.82 KB
/
15_CountDistinctSlices.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
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
"""
CountDistinctSlices
Count the number of distinct slices (containing only unique numbers).
https://app.codility.com/programmers/lessons/15-caterpillar_method/count_distinct_slices/
An integer M and a non-empty array A consisting of N non-negative integers are given.
All integers in array A are less than or equal to M.
A pair of integers (P, Q), such that 0 ≤ P ≤ Q < N, is called a slice of array A.
The slice consists of the elements A[P], A[P + 1], ..., A[Q].
A distinct slice is a slice consisting of only unique numbers.
That is, no individual number occurs more than once in the slice.
For example, consider integer M = 6 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 5
A[3] = 5
A[4] = 2
There are exactly nine distinct slices: (0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2), (3, 3), (3, 4) and (4, 4).
The goal is to calculate the number of distinct slices.
Write a function:
def solution(M, A)
that, given an integer M and a non-empty array A consisting of N integers, returns the number of distinct slices.
If the number of distinct slices is greater than 1,000,000,000, the function should return 1,000,000,000.
For example, given integer M = 6 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 5
A[3] = 5
A[4] = 2
the function should return 9, as explained above.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000];
M is an integer within the range [0..100,000];
each element of array A is an integer within the range [0..M].
Copyright 2009–2024 by Codility Limited. All Rights Reserved.
Unauthorized copying, publication or disclosure prohibited.
--------------------------
! a distinct slice cannot be longer than M, as more would need duplicate values.
! watch that Q does not run off the end of the list
! watch that the count does not run over 1,000,000,000
The 70% solution comes relatively 'easy' but the 100% solution was too hard for me.
For 70% invokes two loops over the sequence which mark a 'sliding window' over
the array. We loop over the values in the window to count the the unique sequences.
https://app.codility.com/demo/results/trainingUSRTK6-NWX/
The 100% solution requires two intertwined 'aha!' moments:
1. introduce a data structure which tracks the values currently inside the window
2. replace the innermost loop, to count sequences inside the window, with a
formula
The data structure, which I've called 'sequence', is a list of booleans which
mark whether or not that value is already inside the window. With this we
can decide whether it is time to extend the right side of the window, because
the next number is not already inside the window, or not. The 'caterpiller' will gooble
up numbers to the right so long as they are not duplicates, then will bring up
tail until the duplicate drops out of the window.
When approached this way, just by maths, the number of sequences to add to the
count is a factor of the length of the sequence. So, rather than looping
to identify each and every one, we can simply add the length of the sequence to
the count.
https://app.codility.com/demo/results/trainingJURN9A-8VM/
The debug for their example looks like:
end that moved, value added/dropped, left index, right index, window values, count, sequence
right 3 0 0 [] 0 [False, False, False, False, False, False, False]
right 4 0 1 [3] 1 [False, False, False, True, False, False, False]
right 5 0 2 [3, 4] 3 [False, False, False, True, True, False, False]
left 3 0 3 [3, 4, 5] 6 [False, False, False, True, True, True, False]
left 4 1 3 [4, 5] 6 [False, False, False, False, True, True, False]
left 5 2 3 [5] 6 [False, False, False, False, False, True, False]
right 5 3 3 [] 6 [False, False, False, False, False, False, False]
right 2 3 4 [5] 7 [False, False, False, False, False, True, False]
left 5 3 5 [5, 2] 9 [False, False, True, False, False, True, False]
left 2 4 5 [2] 9 [False, False, True, False, False, False, False]
"""
import unittest
def solution(m: [int], a: [list]) -> [int]:
"""Return a count of the 'distinct slices'.
while backend not finished
if moving forward
add to count
note the new value in sequence
inch frontend forward
else
drop the tail value from sequence
inch backend forward
"""
left, right = 0, 0
sequence = [False] * (m + 1)
n, count = len(a), 0
while left < n:
if right < n and not sequence[a[right]]:
print('right', a[right], left, right, a[left:right], count, sequence)
count += right - left + 1
sequence[a[right]] = True
right += 1
else:
print('left', a[left], left, right, a[left:right], count, sequence)
sequence[a[left]] = False
left += 1
if count >= 1000000000:
return 1000000000
return count
def solution70(m: [int], a: [list]) -> [int]:
"""Return a count of the 'distinct slices'.
move the left thru every position
move the right thru every position
if the new value is a duplicate
stop going right and bring up the left
else
increment the counter
"""
result = 0
for left in range(len(a)):
for right in range(left + 1, len(a)):
print(left, right, a[right], a[left:right])
if a[right] in a[left:right]:
break
result += 1
if result >= 1000000000:
return 1000000000
return result + len(a)
class TestExercise(unittest.TestCase):
"""
"""
def test_examples(self):
self.assertEqual(solution(0, [0]), 1)
print()
self.assertEqual(9, solution(6, [3, 4, 5, 5, 2]))
print()
self.assertEqual(11, solution(6, [3, 4, 5, 4, 2]))
if __name__ == "__main__":
unittest.main()