Skip to content

Commit f68c64d

Browse files
committed
8/20/23
- Anki bump - replace_and_remove.py - spreadsheet_encoding.py - substring_match.py
1 parent 01138f3 commit f68c64d

File tree

5 files changed

+178
-9
lines changed

5 files changed

+178
-9
lines changed

anki/Competitive Programming.apkg

5.64 KB
Binary file not shown.

elements-of-programming-interviews/problem_mapping.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ problem_mapping = {
518518
"total": 10000
519519
},
520520
"Python: spreadsheet_encoding.py": {
521-
"passed": 0,
521+
"passed": 10000,
522522
"total": 10000
523523
}
524524
},
@@ -532,7 +532,7 @@ problem_mapping = {
532532
"total": 501
533533
},
534534
"Python: replace_and_remove.py": {
535-
"passed": 0,
535+
"passed": 501,
536536
"total": 501
537537
}
538538
},
@@ -644,7 +644,7 @@ problem_mapping = {
644644
"total": 835
645645
},
646646
"Python: substring_match.py": {
647-
"passed": 0,
647+
"passed": 835,
648648
"total": 835
649649
}
650650
}

elements-of-programming-interviews/python/replace_and_remove.py

+77-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,79 @@
55
from test_framework.test_utils import enable_executor_hook
66

77

8+
"""
9+
input: size (int), list of strings
10+
output: int, size of new list? (assume so for now)
11+
Can assume sufficient size (lists aren't immutable anyway)
12+
13+
Edge cases:
14+
- empty string
15+
- no b's or a's
16+
- all b's, all a's
17+
18+
Remove every b, replace every a with 2 ds
19+
- Trivial: O(n) storage, write to new string
20+
- Better if we can use O(c) storage
21+
- can't do forward pass, deletes characters we've not yet examined
22+
- can safely assume enough space for final result
23+
24+
- if we first delete every b, we have just a string with chars and ds,
25+
should give pretty wide margin (may not, though)
26+
- worst edge case: array is 12 a's with 24 spaces; but I don't think
27+
it could catch up unless there wasn't enough room
28+
29+
plan (3 O(n) passes):
30+
- delete b's and downshift;
31+
- definitely will have space starting at right end; start from last character for a replacement
32+
- then do a pass to downshift
33+
"""
34+
35+
import string
36+
37+
38+
def delete_bs(size: int, s: List[str]) -> int:
39+
j = 0
40+
i = 0
41+
while i < size:
42+
if s[i] != 'b':
43+
s[j] = s[i]
44+
j += 1
45+
i += 1
46+
return j
47+
48+
49+
def reversed_replace(size: int, s: List[str]) -> int:
50+
i, j = size-1, len(s)-1
51+
while i >= 0:
52+
if s[i] != 'a' and s[i] not in string.whitespace:
53+
s[j] = s[i]
54+
j -= 1
55+
else:
56+
s[j] = 'd'
57+
j -= 1
58+
s[j] = 'd'
59+
j -= 1
60+
i -= 1
61+
# print(f"{i}: {s}")
62+
return j+1
63+
64+
65+
def leftshift(start: int, s: List[str]):
66+
i = 0
67+
while start < len(s):
68+
s[i] = s[start]
69+
i += 1
70+
start += 1
71+
return i
72+
73+
874
def replace_and_remove(size: int, s: List[str]) -> int:
9-
# TODO - you fill in here.
10-
return 0
75+
# print(f"size: {size}")
76+
new_size = delete_bs(size, s)
77+
new_start = reversed_replace(new_size, s)
78+
# print(f"new start: {new_start}")
79+
new_size = leftshift(new_start, s)
80+
return new_size
1181

1282

1383
@enable_executor_hook
@@ -17,6 +87,11 @@ def replace_and_remove_wrapper(executor, size, s):
1787

1888

1989
if __name__ == '__main__':
90+
"""
91+
# s = ["a", "b", "a", "b", "a", "b", "a", "b", "", "", "", "", "", "", ""]
92+
# print(replace_and_remove(8, s))
93+
# print(s)
94+
"""
2095
exit(
2196
generic_test.generic_test_main('replace_and_remove.py',
2297
'replace_and_remove.tsv',

elements-of-programming-interviews/python/spreadsheet_encoding.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
from test_framework import generic_test
22

3+
"""
4+
Input: string (could be large, but fits in mem)
5+
Output: int (same constraints; represents string)
6+
7+
- "" invalid
8+
- A = 1, Z = 26
9+
- AA = 1*(26^0) + 1*(26*1)
10+
- No 0, no negatives
11+
"""
12+
import string
13+
14+
DIGITS = {c: i+1 for i, c in enumerate(string.ascii_uppercase)}
15+
316

417
def ss_decode_col_id(col: str) -> int:
5-
# TODO - you fill in here.
6-
return 0
18+
total = 0
19+
place = 1
20+
for c in col[::-1]:
21+
total += (DIGITS[c] * place)
22+
place *= 26
23+
return total
724

825

926
if __name__ == '__main__':

elements-of-programming-interviews/python/substring_match.py

+79-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,86 @@
11
from test_framework import generic_test
22

33

4+
"""
5+
Brute force: check every substring in t of len(s). O(n^2) time.
6+
7+
Naive approach; for each character in t, check if it's the first character in s. If so,
8+
see if the next in t is a match on the next in s. Continue this until a match or failure.
9+
If a failure occurs, don't go back to the start of t (we already checked all characters between
10+
it and the failure); continue from failure. O(n) time, O(c) space. Does this work? No.
11+
12+
This approach fails if starting the pattern match at s[i] ends at s[i+n] in failure
13+
so we skip over all chars between s[i] and s[i+n], but would've found a match if we started
14+
at s[i+m] where m < n. When might this occur? If all preceding n-1 characters matched,
15+
but the nth did not, and we would somehow have to be able to "slide P forward in T" and
16+
obtain a match. Example: P = aaacaaacd, T = aaacaaacaaacd.
17+
18+
Knuth-Morris-Pratt: Suppose we followed the naive approach, but instead of resetting p_idx
19+
each time a failure occurs, we "backtrack" to the last location in T where
20+
21+
"""
22+
23+
24+
def compute_lps(pattern):
25+
lps = [0] * len(pattern)
26+
# i is the current char of the prefix,
27+
# j is the current char of the suffix
28+
i, j = 0, 1
29+
while j < len(pattern):
30+
if pattern[j] == pattern[i]:
31+
# a single char of the prefix and suffix (at the end of each)
32+
# matches. We can record it as our best for this position and
33+
# advance each index.
34+
i += 1
35+
lps[j] = i
36+
j += 1
37+
else:
38+
if i == 0:
39+
# If we mismatch and we haven't
40+
# matched a prefix and suffix yet, we can't move
41+
# i forward because we still have to match it to something.
42+
# j moves forward to try the next character.
43+
lps[j] = 0
44+
j += 1
45+
else:
46+
# if we mismatch and we have matched a prefix with the suffix,
47+
# we can't record the best suffix / prefix yet, because of the mismatch.
48+
# instead, we have to send the prefix back to where it was when we got the
49+
# best prefix/suffix match for the previous character. This may send us back to 0.
50+
i = lps[i - 1]
51+
52+
return lps
53+
54+
55+
def kmp(t: str, s: str) -> int:
56+
if s == "":
57+
return 0
58+
lps = compute_lps(s)
59+
t_idx = s_idx = 0
60+
while t_idx < len(t):
61+
if t[t_idx] == s[s_idx]:
62+
# Chars match; one step closer to full match
63+
t_idx += 1
64+
s_idx += 1
65+
else:
66+
if s_idx == 0:
67+
# The chars didn't match, but we haven't matched any
68+
# in the pattern yet anyways, so just go to the next
69+
# char of t and try again
70+
t_idx += 1
71+
else:
72+
# This is the crux of the algorithm. Only go back in
73+
# s to the point where we know we will still match,
74+
# because we precomputed matching prefixes and suffixes.
75+
s_idx = lps[s_idx - 1]
76+
if s_idx == len(s):
77+
# Full match; return the index it starts at.
78+
return t_idx - s_idx
79+
return -1
80+
81+
482
def rabin_karp(t: str, s: str) -> int:
5-
# TODO - you fill in here.
6-
return 0
83+
return kmp(t, s)
784

885

986
if __name__ == '__main__':

0 commit comments

Comments
 (0)