├── 1-Algorithmic Toolbox ├── Week1-Algorithmic Warm-up │ ├── 1-Fibonacci Number.py │ ├── 2-last digit of a large fibonacci number.py │ ├── 3-greatest common divisor.py │ ├── 4-least common multiple.py │ ├── 5-Fibonacci number again.py │ ├── 6-last digit of the sum of fibonacci numbers.py │ ├── 7-last digit of the sum of fibonacci numbers again.py │ └── 8-last digit of the sum of squares of Fibonacci numbers.py ├── Week3-Greedy Algorithms │ ├── 1-money change.py │ ├── 2-max value of the loot.py │ ├── 3-car fueling.py │ ├── 4-max advertisement revenue.py │ ├── 5-collecting_signatures.py │ ├── 6-max number of prizes.py │ └── 7-max salary.py ├── Week4-Divide and Conquer │ ├── 1-binary search.py │ ├── 2-majority element.py │ ├── 3-3-way quick sort.py │ ├── 4-number of inversions.py │ ├── 5-organizing a lottery_naive.py │ ├── 5-organizing_a_lottery_faster.py │ └── 6-closest points.py ├── Week5-Dynamic Programming 1 │ ├── 1-money change agian.py │ ├── 2-primitive operations.py │ ├── 3-edit distance.py │ ├── 4-longest common subsequence of 2 sequences.py │ └── 5-longest common subsequence of 3 sequences.py └── Week6-Dynamic Programming 2 │ ├── 1-maximum amount of gold.py │ ├── 2-partitioning souvenirs.py │ └── 3-maximum value of an arithmetic expression.py ├── 2-Data Structures ├── Week1-Basic Data Structures │ ├── 1-check brackets in the code.py │ ├── 2-compute tree height.py │ ├── 3-packet processing.py │ ├── 4-extending stack interface.py │ └── 5-maximum in sliding window.py ├── Week3-Priority Queues and Disjoint Sets │ ├── 1-convert array into heap.py │ ├── 2-parallel processing.py │ └── 3-merging tables.py ├── Week4-Hash Tables and Hash Functions │ ├── 1-phone book.py │ ├── 2-hashing with chains.py │ ├── 3-find pattern in text.py │ ├── 4-substring equality.py │ ├── 5-longest common substring.py │ └── 6-pattern matching with mismatches.py └── Week6-Binary Search Trees │ ├── 1-binary tree traversals.py │ ├── 2-is binary search tree.py │ ├── 2-min max method.py │ ├── 3-is bst hard.py │ ├── 4-splay tree.py │ ├── 5-naive.py │ └── 5-rope structure.py ├── 3-Algorithms on Graphs ├── Week1-Undirected Graphs │ ├── 1-reachability.py │ └── 2-connected components.py ├── Week2-Directed Graphs │ ├── 1-directed acyclic graphs.py │ ├── 2-topological sort.py │ └── 3-strongly connected components.py ├── Week3-Most Direct Route │ ├── 1-breath first search.py │ └── 2-bipartite.py ├── Week4-Fastest Route │ ├── 1-Dijkstra_min-heap.py │ ├── 2-negative cycle.py │ └── 3-infinite arbitrage.py └── Week5-Minimum Spanning Trees │ ├── 1-connecting points.py │ └── 2-clustering.py ├── 4-Algorithms on Strings ├── Week1-Suffix Trees │ ├── 1-constrcut a Trie from patterns.py │ ├── 2-TrieMatching.py │ ├── 3-Extend TrieMatching.py │ ├── 4-construct the suffix tree of a string.py │ └── 5-shortest non-shared substring.py ├── Week2-Burrows–Wheeler Transform and Suffix Arrays │ ├── 1-construct BWT.py │ ├── 2-invert BWT.py │ ├── 3-BWmatching.py │ └── 4-suffix array.py └── Week4-Algorithmic Challenges │ ├── 1-Knuth Morris Pratt.py │ ├── 2-suffix array for a long string.py │ ├── 3-suffix array matching.py │ └── 4-suffix tree from array.py ├── 5-Advanced Algorithms and Complexity ├── Week1-Flows in Networks │ ├── 1-max flow.py │ ├── 2-bipartite matching.py │ └── 3-stock charts.py ├── Week2-Linear Programming │ ├── 1-Gaussian Elimination.py │ ├── 1b.py │ ├── 2-LP brute force.py │ └── 3-Two-Phase Simplex.py ├── Week3-NP-completeness │ ├── 1-3-color to SAT.py │ ├── 2-Hamitonian path to SAT.py │ └── 3-binary LP to SAT.py └── Week4-Coping with NP-completeness │ ├── 1-integrated circuit.py │ ├── 2-bidirectional edge.py │ ├── 3-traveling salesman.py │ └── 4-reschedule exam.py └── README.md /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/1-Fibonacci Number.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = int(input()) 4 | F = [0, 1] 5 | for i in range(2, n + 1): 6 | F.append(F[i - 1] + F[i - 2]) 7 | print(F[n]) 8 | 9 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/2-last digit of a large fibonacci number.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = int(input()) 4 | F = [0, 1] 5 | last = [0, 1] 6 | for i in range(2, 60): 7 | F.append(F[i - 1] + F[i - 2]) 8 | last.append(int(str(F[i])[-1])) 9 | 10 | print(last[n % 60]) 11 | 12 | 13 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/3-greatest common divisor.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = input() 4 | a, b = map(int, n.split()) 5 | 6 | def GCD(a, b): 7 | if b == 0: 8 | return a 9 | else: 10 | r = a % b 11 | return GCD(b, r) 12 | 13 | print(GCD(a, b)) 14 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/4-least common multiple.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = input() 4 | a, b = map(int, n.split()) 5 | 6 | def GCD(a, b): 7 | if b == 0: 8 | return a 9 | else: 10 | r = a % b 11 | return GCD(b, r) 12 | 13 | def LCM(a, b): 14 | return int(a * b / GCD(a, b)) 15 | 16 | print(LCM(a, b)) 17 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/5-Fibonacci number again.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | i = input() 4 | n, m = map(int, i.split()) 5 | 6 | F = [0, 1] 7 | remain = [0, 1] 8 | 9 | i = 2 10 | last = 0 11 | now = 1 12 | while True: 13 | F.append(F[i - 1] + F[i - 2]) 14 | r = F[i] % m 15 | remain.append(r) 16 | last = now 17 | now = r 18 | i = i + 1 19 | if [last, now] == [0, 1]: 20 | break 21 | 22 | print(remain[n % (i-2)]) 23 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/6-last digit of the sum of fibonacci numbers.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | # last digit of the fibonacci numbers, 0 ~ 59 a cycle 4 | F = [0, 1] 5 | last = [0, 1] 6 | for i in range(2, 60): 7 | F.append(F[i - 1] + F[i - 2]) 8 | last.append(int(str(F[i])[-1])) 9 | 10 | # Sn = 2 * F[n] + F[n-1] - 1 11 | n = int(input()) 12 | result = 2 * last[n % 60] + last[(n - 1) % 60] - 1 13 | print(result % 10) 14 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/7-last digit of the sum of fibonacci numbers again.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | # last digit of the fibonacci numbers, 0 ~ 59 a cycle 4 | F = [0, 1] 5 | last = [0, 1] 6 | for i in range(2, 60): 7 | F.append(F[i - 1] + F[i - 2]) 8 | last.append(int(str(F[i])[-1])) 9 | 10 | a = input() 11 | m, n = map(int, a.split()) 12 | q = int( (n - m + 1) / 60 ) 13 | 14 | total = 0 15 | for i in range((m + q * 60), (n + 1)): 16 | total = total + last[i % 60] 17 | 18 | print(total % 10) 19 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week1-Algorithmic Warm-up/8-last digit of the sum of squares of Fibonacci numbers.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | # last digit of the fibonacci numbers, 0 ~ 59 a cycle 4 | F = [0, 1] 5 | last = [0, 1] 6 | for i in range(2, 60): 7 | F.append(F[i - 1] + F[i - 2]) 8 | last.append(int(str(F[i])[-1])) 9 | 10 | n = int(input()) 11 | S = last[n % 60] * last[n % 60] + last[n % 60] * last[(n - 1) % 60] 12 | 13 | print(S % 10) 14 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/1-money change.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | m = int(input()) 4 | changes = [10, 5, 1] 5 | count = 0 6 | for i in range(3): 7 | n = int(m / changes[i]) 8 | count = count + n 9 | m = m - n * changes[i] 10 | 11 | print(count) 12 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/2-max value of the loot.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | c = input() 4 | n, capacity = map(int, c.split()) 5 | value_per_weight = [] 6 | dic = {} 7 | # dic = {v/w: weight} 8 | for i in range(n): 9 | c = input() 10 | v, w = map(int, c.split()) 11 | value_per_weight.append(v / w) 12 | dic[v / w] = w 13 | value_per_weight.sort(reverse=True) 14 | 15 | def get_optimal_value(capacity, dic, value_per_weight): 16 | V = 0 17 | for i in range(n): 18 | if capacity == 0: 19 | return(V) 20 | a = min(capacity, dic[value_per_weight[i]]) 21 | V = V + value_per_weight[i] * a 22 | capacity = capacity - a 23 | return V 24 | 25 | opt_value = get_optimal_value(capacity, dic, value_per_weight) 26 | print("{:.4f}".format(opt_value)) 27 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/3-car fueling.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | # d - distance; m - most m miles on a full tank; n - n gas stations 4 | #i - 每个stations和A的距离;stored in list x 5 | 6 | d = int(input()) 7 | m = int(input()) 8 | n = int(input()) 9 | i = input() 10 | x = [0] + list(map(int, i.split())) + [d] 11 | 12 | def MinRefill(x, m, n): 13 | current = 0 14 | count = 0 15 | while current <= n: 16 | last = current 17 | while current <= n and x[current + 1] - x[last] <= m: 18 | current = current + 1 19 | if current == last: 20 | return -1 21 | if current <= n: 22 | count = count + 1 23 | return count 24 | 25 | 26 | print(MinRefill(x, m, n)) 27 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/4-max advertisement revenue.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = int(input()) 4 | ipt = input() 5 | an = list(map(int, ipt.split())) 6 | ipt = input() 7 | bn = list(map(int, ipt.split())) 8 | 9 | an.sort() 10 | bn.sort() 11 | cn = [] 12 | for i in range(n): 13 | cn.append(an[i] * bn[i]) 14 | 15 | print(sum(cn)) 16 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/5-collecting_signatures.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = int(input()) 4 | seg = [] 5 | for i in range(n): 6 | ipt = input() 7 | seg.append(list(map(int, ipt.split()))) 8 | seg.sort() 9 | # seg:[[start1, end1], [start2, end2], ... ] 10 | 11 | def min_points(seg): 12 | n = len(seg) 13 | current = 0 14 | ends = [] 15 | while current < n: 16 | last = current 17 | while current < n - 1 and seg[current + 1][0] <= seg[last][1]: 18 | current = current + 1 19 | if seg[current][1] < seg[last][1]: 20 | last = current 21 | ends.append(seg[last][1]) 22 | current = current + 1 23 | return ends 24 | 25 | ends = min_points(seg) 26 | print(len(ends)) 27 | for end in ends: 28 | print(end, end = ' ') 29 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/6-max number of prizes.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = int(input()) 4 | 5 | prize = [] 6 | i = 0 7 | while n > 0: 8 | i = i + 1 9 | if n - i <= i: 10 | i = n 11 | prize.append(i) 12 | n = n - i 13 | 14 | print(len(prize)) 15 | for x in prize: 16 | print(x, end =' ') 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week3-Greedy Algorithms/7-max salary.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | n = int(input()) 4 | digits = list(input().split()) 5 | 6 | def max_num(lst): 7 | n = len(lst) 8 | for i in range(n - 1): 9 | for i in range(n - 1 - i): 10 | if lst[i] + lst[i+1] < lst[i + 1] + lst[i]: 11 | lst[i], lst[i + 1] = lst[i + 1], lst[i] 12 | return lst 13 | 14 | digits = max_num(digits) 15 | result = '' 16 | for digit in digits: 17 | result = result + digit 18 | print(result) 19 | 20 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/1-binary search.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | def BinarySearch(A, low, high, key): 4 | if high < low: 5 | return -1 6 | mid = (high + low) // 2 7 | if key == A[mid]: 8 | return mid 9 | if key < A[mid]: 10 | return BinarySearch(A, low, mid - 1, key) 11 | if key > A[mid]: 12 | return BinarySearch(A, mid + 1, high, key) 13 | 14 | 15 | array = input() 16 | n = int(array.split()[0]) 17 | an = list(map(int, array.split()[1:])) 18 | keys = input() 19 | k = int(keys.split()[0]) 20 | bn = list(map(int, keys.split()[1:]))7 21 | 22 | 23 | for b in bn: 24 | index = BinarySearch(an, 0, n - 1, b) 25 | print(index, end = ' ') 26 | 27 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/2-majority element.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | def QuickSort3(A, l, r): 4 | if r <= l: 5 | return 6 | j, i = Partition3(A, l, r) 7 | QuickSort3(A, l, j) 8 | QuickSort3(A, i, r) 9 | 10 | 11 | def Partition3(A, l, r): 12 | x = A[r] 13 | i = l - 1 14 | p = l - 1 15 | j = r 16 | q = r 17 | 18 | while True: 19 | while A[i + 1] < x: 20 | i = i + 1 21 | i = i + 1 22 | while A[j - 1] > x: 23 | j = j - 1 24 | j = j - 1 25 | if i >= j: 26 | break 27 | A[i], A[j] = A[j], A[i] 28 | if A[i] == x: 29 | p = p + 1 30 | A[i], A[p] = A[p] , A[i] 31 | if A[j] == x: 32 | q = q - 1 33 | A[j], A[q] = A[q], A[j] 34 | 35 | A[i], A[r] = A[r], A[i] 36 | 37 | j = i - 1 38 | k = l 39 | while k <= p: 40 | A[k], A[j] = A[j], A[k] 41 | j = j - 1 42 | k = k + 1 43 | 44 | i = i + 1 45 | k = r - 1 46 | while k >= q: 47 | A[k], A[i] = A[i], A[k] 48 | i = i + 1 49 | k = k - 1 50 | 51 | return j, i 52 | 53 | 54 | def majority_element(a, n): 55 | i = 0 56 | while i < n: 57 | count = 1 58 | while i < n - 1 and a[i + 1] == a[i]: 59 | count = count + 1 60 | i = i + 1 61 | if count > n / 2: 62 | return 1 63 | i = i + 1 64 | return 0 65 | 66 | 67 | n = int(input()) 68 | ipt = input() 69 | a = list(map(int, ipt.split())) 70 | QuickSort3(a, 0, n - 1) 71 | print(majority_element(a, n)) 72 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/3-3-way quick sort.py: -------------------------------------------------------------------------------- 1 | # Use Python 3 2 | 3 | def QuickSort3(A, l, r): 4 | if r <= l: 5 | return 6 | j, i = Partition3(A, l, r) 7 | QuickSort3(A, l, j) 8 | QuickSort3(A, i, r) 9 | 10 | 11 | def Partition3(A, l, r): 12 | x = A[r] 13 | i = l - 1 14 | p = l - 1 15 | j = r 16 | q = r 17 | 18 | while True: 19 | while A[i + 1] < x: 20 | i = i + 1 21 | i = i + 1 22 | while A[j - 1] > x: 23 | j = j - 1 24 | j = j - 1 25 | if i >= j: 26 | break 27 | A[i], A[j] = A[j], A[i] 28 | if A[i] == x: 29 | p = p + 1 30 | A[i], A[p] = A[p] , A[i] 31 | if A[j] == x: 32 | q = q - 1 33 | A[j], A[q] = A[q], A[j] 34 | 35 | A[i], A[r] = A[r], A[i] 36 | 37 | j = i - 1 38 | k = l 39 | while k <= p: 40 | A[k], A[j] = A[j], A[k] 41 | j = j - 1 42 | k = k + 1 43 | 44 | i = i + 1 45 | k = r - 1 46 | while k >= q: 47 | A[k], A[i] = A[i], A[k] 48 | i = i + 1 49 | k = k - 1 50 | 51 | return j, i 52 | 53 | 54 | n = int(input()) 55 | ipt = input() 56 | a = list(map(int, ipt.split())) 57 | QuickSort3(a, 0, n - 1) 58 | for e in a: 59 | print(e, end = ' ') 60 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/4-number of inversions.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | 4 | def mergeSort(a, n): 5 | temp_a = [0] * n 6 | return _mergeSort(a, temp_a, 0, n - 1) 7 | 8 | 9 | def _mergeSort(a, temp_a, left, right): 10 | inv_count = 0 11 | if left < right: 12 | mid = (left + right) // 2 13 | inv_count += _mergeSort(a, temp_a, left, mid) 14 | inv_count += _mergeSort(a, temp_a, mid + 1, right) 15 | inv_count += merge(a, temp_a, left, mid, right) 16 | return inv_count 17 | 18 | 19 | def merge(a, temp_a, left, mid, right): 20 | i = left 21 | j = mid + 1 22 | k = left 23 | inv_count = 0 24 | while i <= mid and j <= right: 25 | if a[i] <= a[j]: 26 | temp_a[k] = a[i] 27 | k += 1 28 | i += 1 29 | else: 30 | inv_count += (mid - i + 1) 31 | temp_a[k] = a[j] 32 | k += 1 33 | j += 1 34 | while i <= mid: 35 | temp_a[k] = a[i] 36 | k += 1 37 | i += 1 38 | while j <= right: 39 | temp_a[k] = a[j] 40 | k += 1 41 | j += 1 42 | for i in range(left, right + 1): 43 | a[i] = temp_a[i] 44 | return inv_count 45 | 46 | 47 | n = int(input()) 48 | ipt = input() 49 | A = list(map(int, ipt.split())) 50 | print(mergeSort(A, n)) 51 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/5-organizing a lottery_naive.py: -------------------------------------------------------------------------------- 1 | # use Python3 2 | 3 | ipt = input() 4 | n_seg, n_point = map(int, ipt.split()) 5 | segs = [] 6 | for i in range(n_seg): 7 | ipt = input() 8 | segs.append(list(map(int, ipt.split()))) 9 | ipt = input() 10 | points_origin = list(map(int, ipt.split())) 11 | segs.sort() 12 | points = sorted(points_origin) 13 | 14 | 15 | def covered_segs(points, segs): 16 | p = len(points) 17 | s = len(segs) 18 | counts = {} 19 | j = 0 20 | for i in range(p): 21 | count = 0 22 | if i > 0 and counts[points[i - 1]] != 0: 23 | for j in range(j, j + counts[points[i - 1]]): 24 | if segs[j][0] <= points[i] <= segs[j][1]: 25 | count += 1 26 | j += 1 27 | while j < s and segs[j][0] <= points[i] <= segs[j][1]: 28 | j += 1 29 | count += 1 30 | counts[points[i]] = count 31 | j = j - count 32 | return counts 33 | 34 | 35 | counts = covered_segs(points, segs) 36 | for point in points_origin: 37 | print(counts[point], end=' ') 38 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/5-organizing_a_lottery_faster.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | import sys 3 | 4 | input = sys.stdin.read() 5 | data = list(map(int, input.split())) 6 | s = data[0] 7 | k = data[1] 8 | starts = data[2:2 * s + 2:2] 9 | ends = data[3:2 * s + 2:2] 10 | points = data[2 * s + 2:] 11 | 12 | lst = [] 13 | for point in points: 14 | lst.append((point, 'p')) 15 | for start in starts: 16 | lst.append((start, 'l')) 17 | for end in ends: 18 | lst.append((end, 'r')) 19 | lst.sort() 20 | 21 | counts = [] 22 | left = 0 23 | right = 0 24 | for pair in lst: 25 | if pair[1] == 'p': 26 | counts.append(pair) 27 | continue 28 | if pair[1] == 'l': 29 | left += 1 30 | if pair[1] == 'r': 31 | right += 1 32 | counts.append((left, right)) 33 | 34 | dic = {} 35 | l = 0 36 | r = 0 37 | for pair in counts: 38 | if pair[1] == 'p': 39 | segs = l + right - r - s 40 | dic[pair[0]] = segs 41 | else: 42 | l = pair[0] 43 | r = pair[1] 44 | 45 | for point in points: 46 | print(dic[point], end=' ') 47 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week4-Divide and Conquer/6-closest points.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | import math 3 | 4 | n = int(input()) 5 | points = [] 6 | for i in range(n): 7 | ipt = input() 8 | coordinate = tuple(map(int, ipt.split())) 9 | points.append(coordinate) 10 | points_x_sorted = sorted(points) 11 | 12 | 13 | def getDistance(point1, point2): 14 | d = math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) 15 | return d 16 | 17 | 18 | def bruteForce(points): 19 | n = len(points) 20 | d = getDistance(points[0], points[1]) 21 | for i in range(n - 1): 22 | for j in range(i + 1, n): 23 | d = min(d, getDistance(points[i], points[j])) 24 | return d 25 | 26 | 27 | def stripMin(points_x_sorted, min_d): 28 | len_p = len(points_x_sorted) 29 | mid = len_p //2 # 分割线的位置 30 | mid_value = points_x_sorted[mid][0] # 分割线的x坐标 31 | points_y_sorted = [] 32 | for point in points_x_sorted: 33 | if abs(point[0] - mid_value) < min_d: 34 | points_y_sorted.append(point) 35 | points_y_sorted.sort(key=lambda p:p[1]) 36 | len_strip = len(points_y_sorted) 37 | if len_strip < 2: 38 | return min_d 39 | else: 40 | min_d2 = getDistance(points_y_sorted[0], points_y_sorted[1]) 41 | for i in range(len_strip - 1): 42 | for j in range(i + 1, min(i + 7, len_strip)): # 用min可以包含数列长度<6的情况 43 | min_d2 = min(min_d2, getDistance(points_y_sorted[i], points_y_sorted[j])) 44 | return min_d2 45 | 46 | 47 | def minDistance(points_x_sorted): 48 | len_p = len(points_x_sorted) 49 | if len_p <= 3: 50 | return bruteForce(points_x_sorted) 51 | else: 52 | mid = len_p // 2 53 | min_d_l = minDistance(points_x_sorted[:mid]) 54 | min_d_r = minDistance(points_x_sorted[mid:]) 55 | min_d = min(min_d_l, min_d_r) 56 | min_d2 = stripMin(points_x_sorted, min_d) 57 | result = min(min_d, min_d2) 58 | return result 59 | 60 | 61 | result = minDistance(points_x_sorted) 62 | print("{0:.9f}".format(result)) 63 | 64 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week5-Dynamic Programming 1/1-money change agian.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | money = int(input()) 4 | coins = (1, 3, 4) 5 | 6 | def change(money, coins): 7 | min_coins = [float("inf")]*(money + 1) 8 | min_coins[0] = 0 9 | for i in range(1, money + 1): 10 | for coin in coins: 11 | if i >= coin: 12 | num_coins = min_coins[i - coin] + 1 13 | if num_coins < min_coins[i]: 14 | min_coins[i] = num_coins 15 | return min_coins[money] 16 | 17 | print(change(money, coins)) 18 | 19 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week5-Dynamic Programming 1/2-primitive operations.py: -------------------------------------------------------------------------------- 1 | #Use Python3 2 | 3 | def minOperations(n): 4 | min_operations = [float("inf")]*(n + 1) 5 | min_operations[0: 2] = 0, 0 6 | prev = [0] * (n+1) 7 | for i in range(2, n + 1): 8 | if i % 3 != 0: 9 | temp_3 = float("inf") 10 | else: 11 | temp_3 = min_operations[int(i / 3)] 12 | if i % 2 != 0: 13 | temp_2 = float("inf") 14 | else: 15 | temp_2 = min_operations[int(i / 2)] 16 | min_operations[i] = min(min_operations[i - 1], temp_2, temp_3) + 1 17 | if min_operations[i] == temp_3 + 1: 18 | prev [i] = int(i / 3) 19 | continue 20 | if min_operations[i] == temp_2 + 1: 21 | prev[i] = int(i / 2) 22 | continue 23 | if min_operations[i] == min_operations[i - 1] + 1: 24 | prev[i] = i - 1 25 | return min_operations, prev 26 | 27 | 28 | n = int(input()) 29 | min_operations, prev = minOperations(n) 30 | result = min_operations[n] 31 | print(result) 32 | 33 | sequence = [n] 34 | i = n 35 | while i > 1: 36 | prev_number = prev[i] 37 | sequence.append(prev_number) 38 | i = prev_number 39 | sequence.sort() 40 | for number in sequence: 41 | print(number, end = ' ') 42 | 43 | 44 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week5-Dynamic Programming 1/3-edit distance.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | def editDistance(a,b): 4 | T = [[float("inf")] * (len(b) + 1) for _ in range(len(a) + 1)] 5 | for i in range(len(a) + 1): 6 | T[i][0] = i 7 | for j in range(len(b) + 1): 8 | T[0][j] = j 9 | for i in range(1, len(a) + 1): 10 | for j in range(1, len(b) + 1): 11 | if a[i - 1] == b[j - 1]: 12 | diff = 0 13 | else: 14 | diff = 1 15 | T[i][j] = min(T[i - 1][j] + 1, T[i][j - 1] + 1, T[i - 1][j - 1] + diff) 16 | return T[len(a)][len(b)] 17 | 18 | 19 | a = input() 20 | b = input() 21 | print(editDistance(a, b)) 22 | 23 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week5-Dynamic Programming 1/4-longest common subsequence of 2 sequences.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | 4 | def LCS(a,b): 5 | m = len(a) 6 | n = len(b) 7 | T = [[float('-inf') ] * (n + 1) for _ in range(m + 1)] 8 | for i in range(m + 1): 9 | T[i][0] = 0 10 | for j in range(n + 1): 11 | T[0][j] = 0 12 | for i in range(1, m + 1): 13 | for j in range(1, n + 1): 14 | if a[i - 1] == b[j - 1]: 15 | T[i][j] = T[i - 1][j - 1] + 1 16 | else: 17 | T[i][j] = max(T[i - 1][j], T[i][j - 1]) 18 | print(T) 19 | return T[m][n] 20 | 21 | 22 | na = int(input()) 23 | an = input() 24 | a = an.split() 25 | nb = int(input()) 26 | bn = input() 27 | b = bn.split() 28 | print(LCS(a, b)) 29 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week5-Dynamic Programming 1/5-longest common subsequence of 3 sequences.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | 4 | def LCS3(a, b, c): 5 | m = len(a) 6 | n = len(b) 7 | l = len(c) 8 | T = [[[float("-inf")] * (l + 1) for _ in range(n + 1)] for _ in range(m + 1)] 9 | for i in range(m + 1): 10 | for j in range(n + 1): 11 | for k in range(l + 1): 12 | if i == 0 or j == 0 or k == 0: 13 | T[i][j][k] = 0 14 | for i in range(1, m + 1): 15 | for j in range(1, n + 1): 16 | for k in range(1, l + 1): 17 | if a[i - 1] == b[j - 1] and a[i - 1] == c[k - 1]: 18 | T[i][j][k] = T[i - 1][j - 1][k - 1] + 1 19 | else: 20 | T[i][j][k] = max(T[i-1][j][k], T[i][j - 1][k], T[i][j][k - 1], T[i - 1][j - 1][k], T[i - 1][j][k - 1], T[i][j - 1][k - 1]) 21 | return T[m][n][l] 22 | 23 | 24 | na = int(input()) 25 | an = input() 26 | a = an.split() 27 | nb = int(input()) 28 | bn = input() 29 | b = bn.split() 30 | nc = int(input()) 31 | cn = input() 32 | c = cn.split() 33 | print(LCS3(a, b, c)) 34 | 35 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week6-Dynamic Programming 2/1-maximum amount of gold.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | 4 | def maxWeight(capacity, items): 5 | n = len(items) 6 | T = [[0] * (capacity + 1) for _ in range(n + 1)] 7 | for i in range(1, n + 1): 8 | for w in range(1, capacity + 1): 9 | T[i][w] = T[i - 1][w] 10 | if w >= items[i - 1]: 11 | weight = T[i - 1][w - items[i - 1]] + items[i - 1] 12 | if weight > T[i][w]: 13 | T[i][w] = weight 14 | print(T) 15 | return T[n][capacity] 16 | 17 | 18 | def optimalSolution(T, capacity, items): 19 | n = len(items) 20 | i = n 21 | w = capacity 22 | used_items = [] 23 | while i > 0 and w > 0: 24 | if T[i][w] == T[i - 1][w]: 25 | i = i - 1 26 | else: 27 | used_items.append(i - 1) 28 | w = w - items[i - 1] 29 | i = i - 1 30 | return used_items 31 | 32 | 33 | ipt = input() 34 | W, n = map(int, ipt.split()) 35 | ipt = input() 36 | weights = list(map(int, ipt.split())) 37 | print(maxWeight(W, weights)) 38 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week6-Dynamic Programming 2/2-partitioning souvenirs.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | 4 | def maxWeight(capacity, items): 5 | n = len(items) 6 | T = [[0] * (capacity + 1) for _ in range(n + 1)] 7 | for i in range(1, n + 1): 8 | for w in range(1, capacity + 1): 9 | T[i][w] = T[i - 1][w] 10 | if w >= items[i - 1]: 11 | weight = T[i - 1][w - items[i - 1]] + items[i - 1] 12 | if weight > T[i][w]: 13 | T[i][w] = weight 14 | return T 15 | 16 | 17 | def optimalSolution(T, capacity, items): 18 | n = len(items) 19 | i = n 20 | w = capacity 21 | used_items = [] 22 | while i > 0 and w > 0: 23 | if T[i][w] == T[i - 1][w]: 24 | i = i - 1 25 | else: 26 | used_items.append(i - 1) 27 | w = w - items[i - 1] 28 | i = i - 1 29 | return used_items 30 | 31 | 32 | def partitionK(capacity, items,k): 33 | if capacity % k != 0: 34 | return 0 35 | else: 36 | capacity = capacity // k 37 | for bag in range(k - 1): 38 | T = maxWeight(capacity, items) 39 | print('weight = ', T[len(items)][capacity]) 40 | if T[len(items)][capacity] != capacity: 41 | return 0 42 | else: 43 | used_items = optimalSolution(T, capacity, items) 44 | for index in used_items: 45 | items.pop(index) 46 | print('items = ', items) 47 | return 1 48 | 49 | 50 | n = int(input()) 51 | ipt = input() 52 | nums = list(map(int, ipt.split())) 53 | total = sum(nums) 54 | print(partitionK(total, nums, 3)) 55 | -------------------------------------------------------------------------------- /1-Algorithmic Toolbox/Week6-Dynamic Programming 2/3-maximum value of an arithmetic expression.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | import operator 3 | 4 | ops = { 5 | '+': operator.add, 6 | '-': operator.sub, 7 | '*': operator.mul, 8 | '/': operator.truediv 9 | } 10 | 11 | 12 | def MinAndMax(i, j, m, M, operation): 13 | minimum = float("+inf") 14 | maximum = float("-inf") 15 | for k in range(i, j): 16 | a = ops[operation[k - 1]](M[i][k], M[k+1][j]) 17 | b = ops[operation[k - 1]](M[i][k], m[k+1][j]) 18 | c = ops[operation[k - 1]](m[i][k], M[k+1][j]) 19 | d = ops[operation[k - 1]](m[i][k], m[k+1][j]) 20 | minimum = min(minimum, a, b, c, d) 21 | maximum = max(maximum, a, b, c, d) 22 | return minimum, maximum 23 | 24 | 25 | def maxValue(digit): 26 | n = len(digit) 27 | m = [[0]* (n + 1) for _ in range(n + 1)] 28 | M = [[0]* (n + 1) for _ in range(n + 1)] 29 | for i in range(1, n + 1): 30 | m[i][i] = digit[i - 1] 31 | M[i][i] = digit[i - 1] 32 | for s in range(1, n): # i和j不能相等 33 | for i in range(1, n + 1 - s): 34 | j = i + s 35 | m[i][j], M[i][j] = MinAndMax(i, j, m, M, operation) 36 | return M[1][n] 37 | 38 | 39 | expression = input() 40 | n = len(expression) 41 | digit = [int(expression[i]) for i in range(0, n + 1, 2)] 42 | operation = [expression[i] for i in range(1, n, 2)] 43 | print(maxValue(digit)) 44 | -------------------------------------------------------------------------------- /2-Data Structures/Week1-Basic Data Structures/1-check brackets in the code.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | 4 | from collections import namedtuple 5 | 6 | Bracket = namedtuple("Bracket", ["char", "position"]) 7 | 8 | 9 | def are_matching(left, right): 10 | return (left + right) in ["()", "[]", "{}"] 11 | 12 | 13 | def find_mismatch(text): 14 | opening_brackets_stack = [] 15 | for i, next in enumerate(text): 16 | if next in "([{": 17 | bracket = Bracket(next, i) 18 | opening_brackets_stack.append(bracket) 19 | if next in ")]}": 20 | if not opening_brackets_stack: 21 | return i + 1 22 | top = opening_brackets_stack.pop() 23 | if (top.char == '(' and next != ')') or (top.char == '[' and next != ']') or (top.char == '{' and next != '}'): 24 | return i + 1 25 | if opening_brackets_stack: 26 | return opening_brackets_stack[0].position + 1 27 | return "Success" 28 | 29 | 30 | def main(): 31 | text = input() 32 | mismatch = find_mismatch(text) 33 | print(mismatch) 34 | 35 | 36 | if __name__ == "__main__": 37 | main() 38 | -------------------------------------------------------------------------------- /2-Data Structures/Week1-Basic Data Structures/2-compute tree height.py: -------------------------------------------------------------------------------- 1 | # Use Python3 2 | 3 | import sys 4 | import threading 5 | 6 | 7 | def compute_height(n, parents): 8 | T = [None] * n 9 | root = parents.index(-1) 10 | T[root] = 1 11 | for vertex in range(n): 12 | height = 0 13 | current = vertex 14 | while current != -1: 15 | height += 1 16 | current = parents[current] 17 | if T[current]: 18 | T[vertex] = T[current] + height 19 | break 20 | return max(T) 21 | 22 | 23 | def main(): 24 | n = int(input()) 25 | parents = list(map(int, input().split())) 26 | print(compute_height(n, parents)) 27 | 28 | 29 | # In Python, the default limit on recursion depth is rather low, 30 | # so raise it here for this problem. Note that to take advantage 31 | # of bigger stack, we have to launch the computation in a new thread. 32 | sys.setrecursionlimit(10 ** 7) # max depth of recursion 33 | threading.stack_size(2 ** 27) # new thread will get stack of such size 34 | threading.Thread(target=main).start() 35 | -------------------------------------------------------------------------------- /2-Data Structures/Week1-Basic Data Structures/3-packet processing.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import namedtuple 5 | import collections 6 | 7 | 8 | Request = namedtuple("Request", ["arrived_at", "time_to_process"]) 9 | Response = namedtuple("Response", ["was_dropped", "started_at"]) 10 | 11 | 12 | class Buffer: 13 | def __init__(self, size): 14 | self.size = size 15 | self.finish_time = collections.deque(maxlen=size) 16 | 17 | def process(self, request): 18 | while len(self.finish_time) > 0: 19 | if self.finish_time[0] <= request.arrived_at: 20 | self.finish_time.popleft() 21 | else: 22 | break 23 | packets = len(self.finish_time) 24 | if packets == self.size: 25 | return Response(True, -1) 26 | if packets == 0: 27 | started_at = request.arrived_at 28 | else: 29 | started_at = self.finish_time[packets - 1] 30 | finished_at = started_at + request.time_to_process 31 | self.finish_time.append(finished_at) 32 | return Response(False, started_at) 33 | 34 | 35 | def process_requests(requests, buffer): 36 | responses = [] 37 | for request in requests: 38 | responses.append(buffer.process(request)) 39 | return responses 40 | 41 | 42 | def main(): 43 | buffer_size, n_requests = map(int, input().split()) 44 | requests = [] 45 | for _ in range(n_requests): 46 | arrived_at, time_to_process = map(int, input().split()) 47 | requests.append(Request(arrived_at, time_to_process)) 48 | 49 | buffer = Buffer(buffer_size) 50 | responses = process_requests(requests, buffer) 51 | 52 | for response in responses: 53 | print(response.started_at if not response.was_dropped else -1) 54 | 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /2-Data Structures/Week1-Basic Data Structures/4-extending stack interface.py: -------------------------------------------------------------------------------- 1 | #python3 2 | 3 | 4 | import sys 5 | 6 | 7 | class StackWithMax(): 8 | def __init__(self): 9 | self.__stack = [] 10 | self.max_value = [] 11 | 12 | def Push(self, a): 13 | if not self.max_value: 14 | self.max_value.append(a) 15 | elif a >= self.max_value[-1]: 16 | self.max_value.append(a) 17 | self.__stack.append(a) 18 | 19 | def Pop(self): 20 | assert(len(self.__stack)) 21 | if self.__stack[-1] == self.max_value[-1]: 22 | self.max_value.pop() 23 | self.__stack.pop() 24 | 25 | def Max(self): 26 | return self.max_value[-1] 27 | 28 | 29 | if __name__ == '__main__': 30 | stack = StackWithMax() 31 | 32 | num_queries = int(sys.stdin.readline()) 33 | for _ in range(num_queries): 34 | query = sys.stdin.readline().split() 35 | 36 | if query[0] == "push": 37 | stack.Push(int(query[1])) 38 | elif query[0] == "pop": 39 | stack.Pop() 40 | elif query[0] == "max": 41 | print(stack.Max()) 42 | else: 43 | assert(0) 44 | -------------------------------------------------------------------------------- /2-Data Structures/Week1-Basic Data Structures/5-maximum in sliding window.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | def maxSlidingWindow(sequence, m): 8 | dq = deque() 9 | max_nums = [] 10 | length = len(sequence) 11 | for i in range(length): 12 | while dq and sequence[i] >= sequence[dq[-1]]: 13 | dq.pop() 14 | dq.append(i) 15 | if i >= m and dq and dq[0] == i - m: 16 | dq.popleft() 17 | if i >= m - 1: 18 | max_nums.append(sequence[dq[0]]) 19 | return max_nums 20 | 21 | 22 | if __name__ == '__main__': 23 | n = int(input()) 24 | input_sequence = [int(i) for i in input().split()] 25 | assert len(input_sequence) == n 26 | window_size = int(input()) 27 | print(*maxSlidingWindow(input_sequence, window_size)) 28 | 29 | -------------------------------------------------------------------------------- /2-Data Structures/Week3-Priority Queues and Disjoint Sets/1-convert array into heap.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | class MinHeap: 5 | def __init__(self, array): 6 | self.A = array 7 | self.size = len(self.A) 8 | self.swaps = [] 9 | 10 | 11 | def SiftDown(self, i): 12 | min_index = i 13 | left = 2 * i + 1 # left child 14 | right = 2 * i + 2 # right child 15 | if left < self.size and self.A[left] < self.A[min_index]: 16 | min_index = left 17 | if right < self.size and self.A[right] < self.A[min_index]: 18 | min_index = right 19 | if min_index != i: 20 | self.swaps.append((i, min_index)) 21 | self.A[i], self.A[min_index] = self.A[min_index], self.A[i] 22 | self.SiftDown(min_index) 23 | 24 | def BuildHeap(self): 25 | n = self.size 26 | for i in range(n // 2 - 1, -1, -1): 27 | self.SiftDown(i) 28 | 29 | 30 | def main(): 31 | n = int(input()) 32 | array = list(map(int, input().split())) 33 | assert len(array) == n 34 | 35 | heap = MinHeap(array) 36 | MinHeap.BuildHeap(heap) 37 | swaps = heap.swaps 38 | print(len(swaps)) 39 | for swap in swaps: 40 | print(*swap) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /2-Data Structures/Week3-Priority Queues and Disjoint Sets/2-parallel processing.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import namedtuple 5 | 6 | AssignedJob = namedtuple('AssignedJob', ['worker', 'started_at']) 7 | 8 | 9 | class JobQueue: 10 | def __init__(self, n_workers, jobs): 11 | self.n = n_workers 12 | self.jobs = jobs 13 | self.finish_time = [] 14 | self.assigned_jobs = [] 15 | for i in range(self.n): 16 | self.finish_time.append([i, 0]) 17 | 18 | def SiftDown(self, i): 19 | min_index = i 20 | left = 2 * i + 1 21 | right = 2 * i + 2 22 | if left < self.n: 23 | if self.finish_time[min_index][1] > self.finish_time[left][1]: 24 | min_index = left 25 | elif self.finish_time[min_index][1] == self.finish_time[left][1]: 26 | if self.finish_time[min_index][0] > self.finish_time[left][0]: 27 | min_index = left 28 | if right < self.n: 29 | if self.finish_time[min_index][1] > self.finish_time[right][1]: 30 | min_index = right 31 | elif self.finish_time[min_index][1] == self.finish_time[right][1]: 32 | if self.finish_time[min_index][0] > self.finish_time[right][0]: 33 | min_index = right 34 | if min_index != i: 35 | self.finish_time[i], self.finish_time[min_index] = self.finish_time[min_index], self.finish_time[i] 36 | self.SiftDown(min_index) 37 | 38 | def NextWorker(self, job): 39 | root = self.finish_time[0] 40 | next_worker = root[0] 41 | started_at = root[1] 42 | self.assigned_jobs.append(AssignedJob(next_worker,started_at)) 43 | self.finish_time[0][1] += job 44 | self.SiftDown(0) 45 | 46 | 47 | def main(): 48 | n_workers, n_jobs = map(int, input().split()) 49 | jobs = list(map(int, input().split())) 50 | assert len(jobs) == n_jobs 51 | 52 | job_queue = JobQueue(n_workers, jobs) 53 | for job in jobs: 54 | job_queue.NextWorker(job) 55 | assigned_jobs = job_queue.assigned_jobs 56 | 57 | for job in assigned_jobs: 58 | print(job.worker, job.started_at) 59 | 60 | 61 | if __name__ == "__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /2-Data Structures/Week3-Priority Queues and Disjoint Sets/3-merging tables.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | class DataBases: 5 | def __init__(self, row_counts): 6 | self.max_row_count = max(row_counts) 7 | self.row_counts = row_counts 8 | n_tables = len(row_counts) 9 | self.parent = list(range(n_tables)) 10 | self.rank = [0] * n_tables 11 | 12 | def FindParent(self, i): 13 | if i != self.parent[i]: 14 | self.parent[i] = self.FindParent(self.parent[i]) 15 | return self.parent[i] 16 | 17 | def MergeTables(self, dst, src): 18 | src_parent = self.FindParent(src) 19 | dst_parent = self.FindParent(dst) 20 | if src_parent == dst_parent: 21 | return 22 | if self.rank[src_parent] > self.rank[dst_parent]: 23 | self.parent[dst_parent] = src_parent 24 | self.row_counts[src_parent] += self.row_counts[dst_parent] 25 | self.row_counts[dst_parent] = 0 26 | self.max_row_count = max(self.max_row_count, self.row_counts[src_parent]) 27 | else: 28 | self.parent[src_parent] = dst_parent 29 | self.row_counts[dst_parent] += self.row_counts[src_parent] 30 | self.row_counts[src_parent] = 0 31 | self.max_row_count = max(self.max_row_count, self.row_counts[dst_parent]) 32 | if self.rank[src_parent] == self.rank[dst_parent]: 33 | self.rank[dst_parent] += 1 34 | 35 | 36 | def main(): 37 | n_tables, n_queries = map(int, input().split()) 38 | counts = list(map(int, input().split())) 39 | assert(n_tables == len(counts)) 40 | db = DataBases(counts) 41 | for i in range(n_queries): 42 | dst, src = map(int, input().split()) 43 | db.MergeTables(dst - 1, src - 1) 44 | print('parent: ', db.parent) 45 | print('row counts: ', db.row_counts) 46 | print(db.max_row_count) 47 | 48 | 49 | 50 | if __name__ == "__main__": 51 | main() 52 | -------------------------------------------------------------------------------- /2-Data Structures/Week4-Hash Tables and Hash Functions/1-phone book.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | class Query: 5 | def __init__(self, query): 6 | self.type = query[0] 7 | self.number = query[1] 8 | if self.type == "add": 9 | self.name = query[2] 10 | 11 | 12 | def WriteResponses(result): 13 | print('\n'.join(result)) 14 | 15 | 16 | def ProcessQuery(query, contacts): 17 | if query.type == "add": 18 | contacts[query.number] = query.name 19 | elif query.type == "del": 20 | if contacts.__contains__(query.number): 21 | del contacts[query.number] 22 | else: 23 | response = 'not found' 24 | if contacts.__contains__(query.number): 25 | response = contacts[query.number] 26 | return response 27 | 28 | 29 | n_queries = int(input()) 30 | contacts = {} 31 | for _ in range(n_queries): 32 | query = Query(input().split()) 33 | result = ProcessQuery(query, contacts) 34 | if result: 35 | print(result) 36 | -------------------------------------------------------------------------------- /2-Data Structures/Week4-Hash Tables and Hash Functions/2-hashing with chains.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | from collections import deque 4 | 5 | 6 | class Query: 7 | def __init__(self, query): 8 | self.type = query[0] 9 | if self.type == "check": 10 | self.index = int(query[1]) 11 | else: 12 | self.word = query[1] 13 | 14 | 15 | class QueryProcessor: 16 | _multiplier = 263 17 | _prime = 1000000007 18 | 19 | def __init__(self, bucket_count): 20 | self.bucket_count = bucket_count 21 | self.hash_table = list(deque() for _ in range(self.bucket_count)) 22 | 23 | def HashFunction(self, word): 24 | hash_value = 0 25 | for char in reversed(word): 26 | hash_value = (hash_value * self._multiplier + ord(char)) % self._prime 27 | return hash_value % self.bucket_count 28 | 29 | def ProcessQuery(self, query): 30 | if query.type == 'check': 31 | if self.hash_table[query.index]: 32 | print(' '.join(self.hash_table[query.index])) 33 | else: 34 | print() 35 | else: 36 | hash_value = self.HashFunction(query.word) 37 | if query.type == "add": 38 | if query.word not in self.hash_table[hash_value]: 39 | self.hash_table[hash_value].appendleft(query.word) 40 | elif query.type == "del": 41 | if query.word in self.hash_table[hash_value]: 42 | self.hash_table[hash_value].remove(query.word) 43 | elif query.type == "find": 44 | if query.word in self.hash_table[hash_value]: 45 | print('yes') 46 | else: 47 | print('no') 48 | 49 | 50 | if __name__ == '__main__': 51 | n_buckets = int(input()) 52 | hash_table = QueryProcessor(n_buckets) 53 | n_queries = int(input()) 54 | for _ in range(n_queries): 55 | command = Query(input().split()) 56 | hash_table.ProcessQuery(command) 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /2-Data Structures/Week4-Hash Tables and Hash Functions/3-find pattern in text.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def PolyHash(string, prime, multiplier): 5 | hash_value = 0 6 | for i in range(len(string) - 1, -1, -1): 7 | hash_value = (hash_value * multiplier + ord(string[i])) % prime 8 | return hash_value 9 | 10 | 11 | def PrecomputedHashes(text, pattern, prime, multiplier): 12 | t = len(text) 13 | p = len(pattern) 14 | s = text[t - p:] 15 | H = list([] for _ in range(t - p + 1)) 16 | H[t - p] = PolyHash(s, prime, multiplier) 17 | y = 1 18 | for i in range(1, p + 1): 19 | y = (y * multiplier) % prime 20 | for i in range(t - p - 1, -1, -1): 21 | H[i] = (multiplier * H[i + 1] + ord(text[i]) - y * ord(text[i + p])) % prime 22 | return H 23 | 24 | 25 | def RabinKarp(text, pattern): 26 | t = len(text) 27 | p = len(pattern) 28 | prime = 1000000007 29 | multiplier = 236 30 | result = [] 31 | pattern_hash = PolyHash(pattern, prime, multiplier) 32 | hash_substrings = PrecomputedHashes(text, pattern, prime, multiplier) 33 | for i in range(t - p + 1): 34 | if pattern_hash == hash_substrings[i]: 35 | result.append(i) 36 | return result 37 | 38 | 39 | if __name__ == '__main__': 40 | pattern = input() 41 | text = input() 42 | positions = RabinKarp(text, pattern) 43 | for pos in positions: 44 | print(pos, end=' ') 45 | -------------------------------------------------------------------------------- /2-Data Structures/Week4-Hash Tables and Hash Functions/4-substring equality.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def HashTable(s, prime, x): 5 | hash_table = list([] for _ in range(len(s) + 1)) 6 | hash_table[0] = 0 7 | for i in range(1, len(s) + 1): 8 | hash_table[i] = (hash_table[i - 1] * x + ord(s[i - 1])) % prime 9 | return hash_table 10 | 11 | 12 | def HashValue(hash_table, prime, x, start, length): 13 | y = pow(x, length, prime) 14 | hash_value = (hash_table[start + length] - y * hash_table[start]) % prime 15 | return hash_value 16 | 17 | 18 | def AreEqual(table1, table2, prime1, prime2, x, a, b, l): 19 | a_hash1 = HashValue(table1, prime1, x, a, l) 20 | a_hash2 = HashValue(table2, prime2, x, a, l) 21 | b_hash1 = HashValue(table1, prime1, x, b, l) 22 | b_hash2 = HashValue(table2, prime2, x, b, l) 23 | if a_hash1 == b_hash1 and a_hash2 == b_hash2: 24 | return 'Yes' 25 | else: 26 | return 'No' 27 | 28 | 29 | if __name__ == '__main__': 30 | string = input() 31 | n_queries = int(input()) 32 | m = 1000000007 33 | m2 = 1000000009 34 | x = 263 35 | hash_table1 = HashTable(string, m, x) 36 | hash_table2 = HashTable(string, m2, x) 37 | print(hash_table1, hash_table2) 38 | for i in range(n_queries): 39 | a, b, l = map(int, input().split()) 40 | print(AreEqual(hash_table1, hash_table2, m, m2, x, a, b, l)) 41 | -------------------------------------------------------------------------------- /2-Data Structures/Week4-Hash Tables and Hash Functions/5-longest common substring.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def PolyHash(s, prime, multiplier): 5 | hash_value = 0 6 | for i in range(len(s) - 1, -1, -1): 7 | hash_value = (hash_value * multiplier + ord(s[i])) % prime 8 | return hash_value 9 | 10 | 11 | def HashTable(s, p_len, prime, multiplier): 12 | H = list([] for _ in range(len(s) - p_len + 1)) 13 | substring = s[len(s) - p_len:] 14 | H[len(s) - p_len] = PolyHash(substring, prime, multiplier) 15 | y = pow(multiplier, p_len, prime) 16 | for i in range(len(s) - p_len - 1, - 1, - 1): 17 | H[i] = (multiplier * H[i + 1] + ord(s[i]) - y * ord(s[i + p_len])) % prime 18 | return H 19 | 20 | 21 | def HashDict(s, p_len, prime, multiplier): 22 | D = {} 23 | substring = s[len(s) - p_len:] 24 | last = PolyHash(substring, prime, multiplier) 25 | D[last] = len(s) - p_len 26 | y = pow(multiplier, p_len, prime) 27 | for j in range(len(s) - p_len - 1, - 1, - 1): 28 | current = (multiplier * last + ord(s[j]) - y * ord(s[j + p_len])) % prime 29 | D[current] = j 30 | last = current 31 | return D 32 | 33 | 34 | def SearchSubstring(hash_table, hash_dict): 35 | check = False 36 | matches = {} 37 | for i in range(len(hash_table)): 38 | b_start = hash_dict.get(hash_table[i], -1) 39 | if b_start != -1: 40 | check = True 41 | matches[i] = b_start 42 | return check, matches 43 | 44 | 45 | def MaxLength(string_a, string_b, low, high, max_length, aStart, bStart): 46 | # a is long string --> hash table, b is short string --> hash dict 47 | mid = (low + high) // 2 # mid is the length of the tested common substring 48 | if low > high: 49 | return aStart, bStart, max_length 50 | prime1 = 1000000007 51 | prime2 = 1000004249 52 | x = 263 53 | aHash1 = HashTable(string_a, mid, prime1, x) 54 | aHash2 = HashTable(string_a, mid, prime2, x) 55 | bHash1 = HashDict(string_b, mid, prime1, x) 56 | bHash2 = HashDict(string_b, mid, prime2, x) 57 | check1, matches1 = SearchSubstring(aHash1, bHash1) 58 | check2, matches2 = SearchSubstring(aHash2, bHash2) 59 | if check1 and check2: 60 | for a, b in matches1.items(): 61 | temp = matches2.get(a, -1) 62 | if temp != -1: 63 | max_length = mid 64 | aStart, bStart = a, b 65 | del aHash1, aHash2, bHash1, bHash2, matches1, matches2 66 | return MaxLength(string_a, string_b, mid + 1, high, max_length, aStart, bStart) 67 | return MaxLength(string_a, string_b, low, mid - 1, max_length, aStart, bStart) 68 | 69 | 70 | while True: 71 | line = input() 72 | if line == '': 73 | break 74 | else: 75 | s, t = line.split() 76 | k = min(len(s), len(t)) 77 | if len(s) <= len(t): 78 | short_string, long_string = s, t 79 | else: 80 | short_string, long_string = t, s 81 | l, i, j = MaxLength(long_string, short_string, 0, k, 0, 0, 0) 82 | if len(s) <= len(t): 83 | print(i, l, j) 84 | else: 85 | print(l, i, j) 86 | -------------------------------------------------------------------------------- /2-Data Structures/Week4-Hash Tables and Hash Functions/6-pattern matching with mismatches.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | import sys 4 | from collections import deque 5 | 6 | 7 | def HashTable(s, prime, x): 8 | hash_table = list([] for _ in range(len(s) + 1)) 9 | hash_table[0] = 0 10 | for i in range(1, len(s) + 1): 11 | hash_table[i] = (hash_table[i - 1] * x + ord(s[i - 1])) % prime 12 | return hash_table 13 | 14 | 15 | def HashValue(hash_table, prime, x, start, length): 16 | y = pow(x, length, prime) 17 | hash_value = (hash_table[start + length] - y * hash_table[start]) % prime 18 | return hash_value 19 | 20 | 21 | def PreCompute(text, pattern): 22 | global m, x 23 | h1 = HashTable(text, m, x) 24 | h2 = HashTable(pattern, m, x) 25 | return h1, h2 26 | 27 | 28 | def CheckMatch(a_start, length, p_len, k): 29 | global m, h1, h2 30 | stack = deque() 31 | stack.append((a_start, 0, length, 1)) 32 | stack.append((a_start+length, length, p_len-length, 1)) 33 | count = 0 34 | temp = 2 35 | C = 0 36 | while stack: 37 | a, b, L, n = stack.popleft() 38 | u1 = HashValue(h1, m, x, a, L) 39 | v1 = HashValue(h2, m, x, b, L) 40 | if temp != n: 41 | count = C 42 | if u1 != v1: 43 | count += 1 44 | if L > 1: 45 | stack.append((a, b, L//2, n+1)) 46 | stack.append((a + L//2, b + L//2, L - L//2, n+1)) 47 | else: 48 | C += 1 49 | if count > k: 50 | return False 51 | temp = n 52 | if count > k: 53 | return False 54 | else: 55 | return True 56 | 57 | 58 | def Solve(t, p, k): 59 | global h1, h2 60 | h1, h2 = PreCompute(t, p) 61 | pos = [] 62 | for i in range(len(t) - len(p) + 1): 63 | if CheckMatch(i, len(p) // 2, len(p), k): 64 | pos.append(i) 65 | return pos 66 | 67 | 68 | m = 1000000007 69 | x = 263 70 | 71 | for line in sys.stdin.readlines(): 72 | k, t, p = line.split() 73 | results = Solve(t, p, int(k)) 74 | print(len(results), *results) 75 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/1-binary tree traversals.py: -------------------------------------------------------------------------------- 1 | # python 3 2 | 3 | 4 | import sys, threading 5 | 6 | sys.setrecursionlimit(10 ** 6) # max depth of recursion 7 | threading.stack_size(2 ** 27) # new thread will get stack of such size 8 | 9 | 10 | class TreeOrders: 11 | def read(self): 12 | self.n = int(sys.stdin.readline()) 13 | self.key = [0 for i in range(self.n)] 14 | self.left = [0 for i in range(self.n)] 15 | self.right = [0 for i in range(self.n)] 16 | for i in range(self.n): 17 | [a, b, c] = map(int, sys.stdin.readline().split()) 18 | self.key[i] = a 19 | self.left[i] = b 20 | self.right[i] = c 21 | 22 | def inOrder(self, root): 23 | if root == -1: 24 | return [] 25 | self.inOrder(self.left[root]) 26 | print(self.key[root], end=' ') 27 | self.inOrder(self.right[root]) 28 | 29 | def preOrder(self, root): 30 | if root == -1: 31 | return [] 32 | print(self.key[root], end=' ') 33 | self.preOrder(self.left[root]) 34 | self.preOrder(self.right[root]) 35 | 36 | def postOrder(self, root): 37 | if root == -1: 38 | return [] 39 | self.postOrder(self.left[root]) 40 | self.postOrder(self.right[root]) 41 | print(self.key[root], end=' ') 42 | 43 | 44 | def main(): 45 | tree = TreeOrders() 46 | tree.read() 47 | tree.inOrder(0) 48 | print() 49 | tree.preOrder(0) 50 | print() 51 | tree.postOrder(0) 52 | print() 53 | 54 | 55 | threading.Thread(target=main).start() 56 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/2-is binary search tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, threading 4 | 5 | sys.setrecursionlimit(10**7) # max depth of recursion 6 | threading.stack_size(2**25) # new thread will get stack of such size 7 | 8 | 9 | class Node: 10 | def __init__(self, a, b, c): 11 | self.key = a 12 | self.left = b 13 | self.right = c 14 | 15 | 16 | def inOrder(tree): 17 | stack = [] 18 | result = [] 19 | curr = 0 20 | while stack or curr != -1: 21 | if curr != -1: 22 | root = tree[curr] 23 | stack.append(root) 24 | curr = root.left 25 | else: 26 | root = stack.pop() 27 | result.append(root.key) 28 | curr = root.right 29 | return result 30 | 31 | 32 | def IsBinarySearchTree(tree, n): 33 | nodes = inOrder(tree) 34 | for i in range(1, n): 35 | if nodes[i] <= nodes[i - 1]: 36 | return False 37 | return True 38 | 39 | 40 | def main(): 41 | n_nodes = int(input()) 42 | nodes = [0 for _ in range(n_nodes)] 43 | for i in range(n_nodes): 44 | a, b, c = map(int, input().split()) 45 | node = Node(a, b, c) 46 | nodes[i] = node 47 | if n_nodes == 0 or IsBinarySearchTree(nodes, n_nodes): 48 | print('CORRECT') 49 | else: 50 | print('INCORRECT') 51 | 52 | 53 | threading.Thread(target=main).start() 54 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/2-min max method.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, threading 4 | 5 | sys.setrecursionlimit(10**7) # max depth of recursion 6 | threading.stack_size(2**25) # new thread will get stack of such size 7 | 8 | 9 | class Node: 10 | def __init__(self, a, b, c): 11 | self.key = a 12 | self.left = b 13 | self.right = c 14 | 15 | 16 | def IsBinarySearchTree(tree): 17 | stack = [(float('-inf'), tree[0], float('inf'))] 18 | while stack: 19 | min, root, max = stack.pop() 20 | if root.key < min or root.key > max: 21 | return False 22 | if root.left != -1: 23 | stack.append((min, tree[root.left], root.key)) 24 | if root.right != -1: 25 | stack.append((root.key, tree[root.right], max)) 26 | return True 27 | 28 | 29 | def main(): 30 | n_nodes = int(input()) 31 | nodes = [0 for _ in range(n_nodes)] 32 | for i in range(n_nodes): 33 | a, b, c = map(int, input().split()) 34 | node = Node(a, b, c) 35 | nodes[i] = node 36 | if n_nodes == 0 or IsBinarySearchTree(nodes): 37 | print('CORRECT') 38 | else: 39 | print('INCORRECT') 40 | 41 | 42 | threading.Thread(target=main).start() 43 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/3-is bst hard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, threading 4 | 5 | sys.setrecursionlimit(10**7) # max depth of recursion 6 | threading.stack_size(2**25) # new thread will get stack of such size 7 | 8 | 9 | class Node: 10 | def __init__(self, a, b, c): 11 | self.key = a 12 | self.left = b 13 | self.right = c 14 | 15 | 16 | def IsBinarySearchTree(tree): 17 | stack = [(float('-inf'), tree[0], float('inf'))] 18 | while stack: 19 | min, root, max = stack.pop() 20 | if root.key < min or root.key >= max: 21 | return False 22 | if root.left != -1: 23 | stack.append((min, tree[root.left], root.key)) 24 | if root.right != -1: 25 | stack.append((root.key, tree[root.right], max)) 26 | return True 27 | 28 | 29 | def main(): 30 | n_nodes = int(input()) 31 | nodes = [0 for _ in range(n_nodes)] 32 | for i in range(n_nodes): 33 | a, b, c = map(int, input().split()) 34 | node = Node(a, b, c) 35 | nodes[i] = node 36 | if n_nodes == 0 or IsBinarySearchTree(nodes): 37 | print('CORRECT') 38 | else: 39 | print('INCORRECT') 40 | 41 | 42 | threading.Thread(target=main).start() 43 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/4-splay tree.py: -------------------------------------------------------------------------------- 1 | # python 3 2 | 3 | 4 | class Vertex: 5 | def __init__(self, key, sum, left, right, parent): 6 | (self.key, self.sum, self.left, self.right, self.parent) = (key, sum, left, right, parent) 7 | 8 | 9 | def update(v): 10 | if v is None: 11 | return 12 | v.sum = v.key + (v.left.sum if v.left is not None else 0) + (v.right.sum if v.right is not None else 0) 13 | if v.left is not None: 14 | v.left.parent = v 15 | if v.right is not None: 16 | v.right.parent = v 17 | 18 | 19 | def smallRotation(v): 20 | parent = v.parent 21 | if parent is None: 22 | return 23 | grandparent = v.parent.parent 24 | # rotate right 25 | if parent.left == v: 26 | m = v.right 27 | v.right = parent 28 | parent.left = m 29 | # rotate left 30 | else: 31 | m = v.left 32 | v.left = parent 33 | parent.right = m 34 | update(parent) 35 | update(v) 36 | v.parent = grandparent 37 | if grandparent is not None: 38 | if grandparent.left == parent: 39 | grandparent.left = v 40 | else: 41 | grandparent.right = v 42 | 43 | 44 | def bigRotation(v): 45 | # zig-zig right: RotateRight 2 times 46 | if v.parent.left == v and v.parent.parent.left == v.parent: 47 | smallRotation(v.parent) 48 | smallRotation(v) 49 | # zig-zig left: RotateLeft 2 times 50 | elif v.parent.right == v and v.parent.parent.right == v.parent: 51 | smallRotation(v.parent) 52 | smallRotation(v) 53 | # zig-zag: RotateRight 54 | else: 55 | smallRotation(v) 56 | smallRotation(v) 57 | # 把zig和单独放在small rotation里是因为zig的情况没有grandparent,如果直接使用big rotation会报错。 58 | 59 | 60 | # Makes splay of the given vertex and makes it the new root. 61 | def splay(v): 62 | if v is None: 63 | return None 64 | while v.parent is not None: 65 | if v.parent.parent == None: 66 | smallRotation(v) 67 | break 68 | bigRotation(v) 69 | return v # 最后v.parent = None 70 | 71 | 72 | # Searches for the given key in the tree with the given root 73 | # and calls splay for the deepest visited node after that. 74 | # Returns pair of the result and the new root. 75 | # If found, result is a pointer to the node with the given key. 76 | # Otherwise, result is a pointer to the node with the smallest 77 | # bigger key (next value in the order). 78 | # If the key is bigger than all keys in the tree, 79 | # then result is None. 80 | def find(root, key): # key是一个int,不是node 81 | v = root 82 | last = root 83 | next = None 84 | while v is not None: 85 | if v.key >= key and (next is None or v.key < next.key): 86 | next = v 87 | last = v 88 | if v.key == key: 89 | break 90 | if v.key < key: 91 | v = v.right 92 | else: 93 | v = v.left 94 | root = splay(last) 95 | if next is not None: 96 | root = splay(next) 97 | return (next, root) 98 | 99 | 100 | def split(root, key): 101 | (result, root) = find(root, key) 102 | # 根据find(),可知result一定>=key,所以我们把result放到右边的子树上,即CutLeft() 103 | if result is None: 104 | return (root, None) 105 | right = splay(result) 106 | left = right.left 107 | right.left = None 108 | if left is not None: 109 | left.parent = None 110 | update(left) # important!! 111 | update(right) # important!! 112 | return (left, right) 113 | 114 | 115 | def merge(left, right): 116 | if left is None: 117 | return right 118 | if right is None: 119 | return left 120 | # find the smallest node in the right tree, splay the node, 121 | # (Method 2:node = find(right, -inf), splay(node)) 122 | # then merge left tree and right tree 123 | while right.left is not None: 124 | right = right.left 125 | right = splay(right) 126 | right.left = left 127 | left.parent = right # starter file里忘掉了 128 | update(right) # important!! 129 | return right 130 | 131 | 132 | root = None 133 | 134 | 135 | def insert(x): 136 | global root 137 | (left, right) = split(root, x) 138 | new_vertex = None 139 | if right is None or right.key != x: 140 | new_vertex = Vertex(x, x, None, None, None) 141 | root = merge(merge(left, new_vertex), right) 142 | 143 | 144 | def erase(x): 145 | global root 146 | (left, middle) = split(root, x) 147 | (middle, right) = split(middle, x + 1) 148 | root = merge(left, right) 149 | 150 | 151 | def search(x): 152 | global root 153 | (result, root) = find(root, x) 154 | if result is None or result.key != x: 155 | return False 156 | else: 157 | return True 158 | 159 | 160 | def sum(fr, to): 161 | global root 162 | (left, middle) = split(root, fr) 163 | (middle, right) = split(middle, to + 1) 164 | ans = 0 165 | if middle is not None: 166 | ans += middle.sum 167 | root = merge(merge(left, middle), right) 168 | return ans 169 | 170 | 171 | MODULO = 1000000001 172 | n_operations = int(input()) 173 | last_sum_result = 0 174 | for _ in range(n_operations): 175 | line = list(input().split()) 176 | if line[0] == '+': 177 | x = int(line[1]) 178 | insert((x + last_sum_result) % MODULO) 179 | elif line[0] == '-': 180 | x = int(line[1]) 181 | erase((x + last_sum_result) % MODULO) 182 | elif line[0] == '?': 183 | x = int(line[1]) 184 | if search((x + last_sum_result) % MODULO): 185 | print('Found') 186 | else: 187 | print('Not found') 188 | elif line[0] == 's': 189 | l = int(line[1]) 190 | r = int(line[2]) 191 | res = sum((l + last_sum_result) % MODULO, (r + last_sum_result) % MODULO) 192 | print(res) 193 | last_sum_result = res % MODULO 194 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/5-naive.py: -------------------------------------------------------------------------------- 1 | # python3 2 | import sys 3 | 4 | 5 | class Rope: 6 | def __init__(self, s): 7 | self.s = s 8 | 9 | def result(self): 10 | return self.s 11 | 12 | def process(self, i, j, k): 13 | substring = self.s[i:j + 1] 14 | self.s = self.s[:i] + self.s[j + 1:] 15 | if k == 0: 16 | self.s = substring + self.s 17 | else: 18 | self.s = self.s[:k] + substring + self.s[k:] 19 | 20 | 21 | rope = Rope(sys.stdin.readline().strip()) 22 | q = int(sys.stdin.readline()) 23 | for _ in range(q): 24 | i, j, k = map(int, sys.stdin.readline().strip().split()) 25 | rope.process(i, j, k) 26 | print(rope.result()) 27 | -------------------------------------------------------------------------------- /2-Data Structures/Week6-Binary Search Trees/5-rope structure.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | import sys, threading 4 | 5 | sys.setrecursionlimit(10 ** 7) # max depth of recursion 6 | threading.stack_size(2 ** 27) # new thread will get stack of such size 7 | 8 | 9 | # Splay tree implementation 10 | 11 | # Vertex of a splay tree 12 | class Vertex: 13 | def __init__(self, key, size, char, left, right, parent): 14 | (self.key, self.size, self.char, self.left, self.right, self.parent) = (key, size, char, left, right, parent) 15 | 16 | 17 | class Rope: 18 | def __init__(self, s): 19 | global root 20 | self.s = s 21 | for idx, char in enumerate(self.s): 22 | v = Vertex(idx, 1, char, None, None, None) 23 | root = self.merge(root, v) 24 | 25 | def get_size(self, v): 26 | if v == None: 27 | return 0 28 | return v.size 29 | 30 | def update(self, v): 31 | # print("updating") 32 | if v == None: 33 | return 34 | v.size = self.get_size(v.left) + self.get_size(v.right) + 1 35 | if v.left != None: 36 | v.left.parent = v 37 | if v.right != None: 38 | v.right.parent = v 39 | 40 | def smallRotation(self, v): 41 | parent = v.parent 42 | if parent == None: 43 | return 44 | grandparent = v.parent.parent 45 | if parent.left == v: 46 | # rotate to the right; so the previous right child of v is now the left child 47 | # of v's parent (which is now v's right child) 48 | m = v.right 49 | v.right = parent 50 | parent.left = m 51 | else: 52 | # similarly, rotate v to the left; so now its parent becomes its left child 53 | # and any left child of v becomes the former parent's right child 54 | m = v.left 55 | v.left = parent 56 | parent.right = m 57 | self.update(parent) 58 | self.update(v) 59 | v.parent = grandparent 60 | if grandparent != None: 61 | if grandparent.left == parent: 62 | grandparent.left = v 63 | else: 64 | grandparent.right = v 65 | 66 | def bigRotation(self, v): 67 | # if "straight" up either left or right, zig-zig 68 | if v.parent.left == v and v.parent.parent.left == v.parent: 69 | # Zig-zig 70 | self.smallRotation(v.parent) 71 | self.smallRotation(v) 72 | elif v.parent.right == v and v.parent.parent.right == v.parent: 73 | # Zig-zig 74 | self.smallRotation(v.parent) 75 | self.smallRotation(v) 76 | # otherwise zig-zag -- see slides for pictures 77 | else: 78 | # Zig-zag 79 | self.smallRotation(v) 80 | self.smallRotation(v) 81 | 82 | def splay(self, v): 83 | if v == None: 84 | return None 85 | while v.parent != None: 86 | # as long as grandparent exists, bigRotation is always called 87 | # -- small rotation gets called once at the end, followed by loop break 88 | if v.parent.parent == None: 89 | self.smallRotation(v) 90 | break 91 | self.bigRotation(v) 92 | # return the new root node after splay 93 | return v 94 | 95 | def find(self, root, order_stat): 96 | v = root 97 | if v is None: 98 | return None 99 | if v.left is None: 100 | s = 0 101 | else: 102 | s = self.get_size(v.left) 103 | if order_stat == s + 1: 104 | return v 105 | elif order_stat < s + 1: 106 | return self.splay(self.find(v.left, order_stat)) 107 | else: 108 | return self.splay(self.find(v.right, order_stat - s - 1)) 109 | 110 | def split(self, root): 111 | # (result, root) = self.find(root, key) 112 | # if find returned result = None, there are no nodes in the tree with keys above 113 | # the given key, so just return the root node for the single tree and do no 114 | # updates 115 | if root == None: 116 | return (root, None) 117 | 118 | # otherwise, splay the next bigger node and set right new tree node to that node 119 | right = root 120 | # left is a temp var for the child to the left of the splayed node 121 | left = right.left 122 | # then set right.left = None to split the trees effectively 123 | right.left = None 124 | # and ensure that left is now root node for its own tree too 125 | if left != None: 126 | left.parent = None 127 | # update the values on these two nodes 128 | self.update(left) 129 | self.update(right) 130 | # return pointers to the root nodes for two new trees 131 | return (left, right) 132 | 133 | def merge(self, left, right): 134 | # if either of the nodes don't exist, just return the one that does 135 | 136 | if left == None: 137 | # print("cond1") 138 | return right 139 | if right == None: 140 | # print("cond2") 141 | return left 142 | 143 | while right.left != None: 144 | right = right.left 145 | # splay the leftmost node on the "right" tree being merged -- so, the smallest 146 | # value on the bigger tree is now root 147 | right = self.splay(right) 148 | # the right side of the right tree is still good, need to set its left side to 149 | # be the left tree 150 | right.left = left 151 | # update the whole tree (this will take care of sum and settings its chidlrens 152 | # parent pointers) and return 153 | self.update(right) 154 | return right 155 | 156 | def in_order(self): 157 | # print pre-order tree traversal for debugging 158 | def _in_order(r): 159 | if r is None: 160 | return 161 | _in_order(r.left) 162 | 163 | # key_result.append(r.key) 164 | char_result.append(r.char) 165 | # size_result.append(r.size) 166 | _in_order(r.right) 167 | return 168 | 169 | global root 170 | # key_result = [] 171 | char_result = [] 172 | # size_result = [] 173 | _in_order(root) 174 | # return key_result, char_result, size_result 175 | return char_result 176 | 177 | def insert(self, x): 178 | # print("inserting value: %f" % x) 179 | global root 180 | # split the tree starting at root for value x 181 | (left, right) = self.split(root, x) 182 | new_vertex = None 183 | # if x is bigger than the whole tree, or if the right tree doesn't happen to have 184 | # key=x, need to create a new vertex with key (and current sum) of x 185 | if right == None or right.key != x: 186 | new_vertex = Vertex(x, x, None, None, None) 187 | # then merge left with the new_vertex if it was created, then merge again with right 188 | # -- NOTE, this means that if right.key == x aobve, you just merge the two trees 189 | # without creating a new vertex 190 | root = self.merge(self.merge(left, new_vertex), right) 191 | 192 | def result(self): 193 | # pos_list, char_list, size_list = self.in_order() 194 | char_list = self.in_order() 195 | self.s = ''.join(char_list) 196 | return self.s 197 | 198 | def process(self, i, j, k): 199 | i = i + 1 200 | j = j + 1 201 | k = k + 1 202 | global root 203 | result = self.find(root, j + 1) 204 | left, right = self.split(result) 205 | if i == 1: 206 | if result == None: 207 | return 208 | tmp = self.find(right, k) 209 | if tmp is not None: 210 | mid, right = self.split(tmp) 211 | left = self.merge(mid, left) 212 | root = self.merge(left, right) 213 | else: 214 | root = self.merge(right, left) 215 | else: 216 | if result is not None: 217 | tmp = self.find(left, i) 218 | if tmp is None: 219 | print("gotcha") 220 | left, mid = self.split(tmp) 221 | left = self.merge(left, right) 222 | tmp2 = self.find(left, k) 223 | if tmp2 is not None: 224 | left, right = self.split(tmp2) 225 | left = self.merge(left, mid) 226 | root = self.merge(left, right) 227 | else: 228 | root = self.merge(left, mid) 229 | else: 230 | tmp = self.find(root, i) 231 | left, right = self.split(tmp) 232 | tmp2 = self.find(left, k) 233 | if tmp2 is not None: 234 | left, mid = self.split(tmp2) 235 | left = self.merge(left, right) 236 | root = self.merge(left, mid) 237 | else: 238 | root = self.merge(left, right) 239 | 240 | 241 | def main(): 242 | # if __name__ == "__main__": 243 | global root 244 | root = None 245 | rope = Rope(sys.stdin.readline().strip()) 246 | q = int(sys.stdin.readline()) 247 | for _ in range(q): 248 | i, j, k = map(int, sys.stdin.readline().strip().split()) 249 | rope.process(i, j, k) 250 | print(rope.result()) 251 | 252 | 253 | root = None 254 | threading.Thread(target=main).start() 255 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week1-Undirected Graphs/1-reachability.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def reach(adj, visited, x, y): 5 | visited[x] = True 6 | for vertex in adj[x]: 7 | if not visited[vertex]: 8 | reach(adj, visited, vertex, y) 9 | 10 | 11 | if __name__ == '__main__': 12 | n_vertices, n_edges = map(int, input().split()) 13 | edges = [] 14 | adjacency_list = [[] for _ in range(n_vertices + 1)] 15 | for i in range(n_edges): 16 | edges.append(tuple(map(int, input().split()))) 17 | for (a, b) in edges: 18 | adjacency_list[a].append(b) 19 | adjacency_list[b].append(a) 20 | u, v = map(int, input().split()) 21 | visited = [False] * (n_vertices + 1) 22 | reach(adjacency_list, visited, u, v) 23 | if visited[v]: 24 | print(1) 25 | else: 26 | print(0) 27 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week1-Undirected Graphs/2-connected components.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def explore(adj, visited, x): 5 | visited[x] = True 6 | for vertex in adj[x]: 7 | if not visited[vertex]: 8 | explore(adj, visited, vertex) 9 | 10 | 11 | def number_of_components(n_vertices, adj, visited): 12 | n_cc = 0 13 | for i in range(1, n_vertices + 1): 14 | if not visited[i]: 15 | explore(adj, visited, i) 16 | n_cc += 1 17 | # print('vertex:', i, n_cc) 18 | return n_cc 19 | 20 | 21 | 22 | if __name__ == '__main__': 23 | n_vertices, n_edges = map(int, input().split()) 24 | edges = [] 25 | adjacency_list = [[] for _ in range(n_vertices + 1)] 26 | for i in range(n_edges): 27 | edges.append(tuple(map(int, input().split()))) 28 | for (a, b) in edges: 29 | adjacency_list[a].append(b) 30 | adjacency_list[b].append(a) 31 | visited = [False] * (n_vertices + 1) 32 | n_components = number_of_components(n_vertices, adjacency_list, visited) 33 | print(n_components) 34 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week2-Directed Graphs/1-directed acyclic graphs.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def explore(edges, vertex, visited, stack, is_dag): 5 | visited[vertex] = True 6 | stack.append(vertex) 7 | for v in edges[vertex]: 8 | if v in stack: 9 | is_dag[0] = False 10 | if not visited[v]: 11 | explore(edges, v, visited, stack, is_dag) 12 | stack.pop() 13 | 14 | 15 | def is_DAG(edges, visited, n): 16 | is_dag = [True] 17 | stack = [] 18 | for i in range(1, n + 1): 19 | if not visited[i]: 20 | explore(edges, i, visited, stack, is_dag) 21 | if not is_dag[0]: 22 | return False 23 | return True 24 | 25 | 26 | if __name__ == '__main__': 27 | n_vertices, n_edges = map(int, input().split()) 28 | edges = [[] for _ in range(n_vertices + 1)] 29 | for i in range(n_edges): 30 | a, b = map(int, input().split()) 31 | edges[a].append(b) 32 | visited = [False] * (n_vertices + 1) 33 | check = is_DAG(edges, visited, n_vertices) 34 | if check: 35 | print(0) 36 | else: 37 | print(1) 38 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week2-Directed Graphs/2-topological sort.py: -------------------------------------------------------------------------------- 1 | # python 3 2 | 3 | 4 | def explore(graph, vertex, visited, post): 5 | global clock 6 | visited[vertex] = True 7 | for v in graph[vertex]: 8 | if not visited[v]: 9 | explore(graph, v, visited, post) 10 | post[vertex] = clock 11 | clock += 1 12 | 13 | 14 | def topoSort(n, graph, visited, post): 15 | global clock 16 | for i in range(1, n + 1): 17 | if not visited[i]: 18 | explore(graph, i, visited, post) 19 | post = list(enumerate(post[1:], start=1)) 20 | post.sort(key=lambda x:x[1], reverse=True) 21 | return post 22 | 23 | 24 | if __name__ == '__main__': 25 | n_vertices, n_edges = map(int, input().split()) 26 | edges = [[] for _ in range(n_vertices + 1)] 27 | for i in range(n_edges): 28 | a, b = map(int, input().split()) 29 | edges[a].append(b) 30 | visited = [False] * (n_vertices + 1) 31 | postorder = [0] * (n_vertices + 1) 32 | clock = 1 33 | postorder = topoSort(n_vertices, edges, visited, postorder) 34 | for v, post in postorder: 35 | print(v, end=' ') 36 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week2-Directed Graphs/3-strongly connected components.py: -------------------------------------------------------------------------------- 1 | # python 3 2 | 3 | import sys 4 | import threading 5 | 6 | sys.setrecursionlimit(10**7) # max depth of recursion 7 | threading.stack_size(2**25) # new thread will get stack of such size 8 | 9 | 10 | def explore(vertex, graph, visited): 11 | visited[vertex] = True 12 | for v in graph[vertex]: 13 | if not visited[v]: 14 | explore(v, graph, visited) 15 | 16 | 17 | def explore_clock(vertex, graph, visited, post): 18 | global clock 19 | visited[vertex] = True 20 | clock += 1 21 | for v in graph[vertex]: 22 | if not visited[v]: 23 | explore_clock(v, graph, visited, post) 24 | post[vertex] = clock 25 | clock += 1 26 | 27 | 28 | def DFS(n, graph): 29 | global clock 30 | visited = [False] * (n + 1) 31 | post = [0] * (n + 1) 32 | for v in range(1, n + 1): 33 | if not visited[v]: 34 | explore_clock(v, graph, visited, post) 35 | post = list(enumerate(post[1:], start=1)) 36 | post.sort(key=lambda x:x[1], reverse=True) 37 | post_vertex = [] 38 | for v, order in post: 39 | post_vertex.append(v) 40 | return post_vertex 41 | 42 | 43 | def number_of_SCC(n, reverse_graph, graph): 44 | global clock 45 | post_vertex = DFS(n, reverse_graph) 46 | visited = [False] * (n + 1) 47 | n_SCC = 0 48 | for i in post_vertex: 49 | if not visited[i]: 50 | explore(i, graph, visited) 51 | n_SCC += 1 52 | return n_SCC 53 | 54 | 55 | def main(): 56 | global clock 57 | n_vertices, n_edges = map(int, input().split()) 58 | edges = [[] for _ in range(n_vertices + 1)] 59 | reverse_edges = [[] for _ in range(n_vertices + 1)] 60 | for i in range(n_edges): 61 | a, b = map(int, input().split()) 62 | edges[a].append(b) 63 | reverse_edges[b].append(a) 64 | clock = 1 65 | n_SCC = number_of_SCC(n_vertices, reverse_edges, edges) 66 | print(n_SCC) 67 | 68 | 69 | threading.Thread(target=main).start() 70 | 71 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week3-Most Direct Route/1-breath first search.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | def BFS(n, adj, start, end): 8 | dist = [float('inf')] * (n + 1) 9 | queue = deque() 10 | queue.append(start) 11 | dist[start] = 0 12 | while queue: 13 | now = queue.popleft() 14 | for vertex in adj[now]: 15 | if dist[vertex] == float('inf'): 16 | queue.append(vertex) 17 | dist[vertex] = dist[now] + 1 18 | return dist[end] 19 | 20 | 21 | if __name__ == '__main__': 22 | n_vertices, n_edges = map(int, input().split()) 23 | adjacency_list = [[] for _ in range(n_vertices + 1)] 24 | for i in range(n_edges): 25 | a, b = map(int, input().split()) 26 | adjacency_list[a].append(b) 27 | adjacency_list[b].append(a) 28 | u, v = map(int, input().split()) 29 | distance = BFS(n_vertices, adjacency_list, u, v) 30 | if distance == float('inf'): 31 | print(-1) 32 | else: 33 | print(distance) 34 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week3-Most Direct Route/2-bipartite.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | def isBipartite(n, adj): 8 | dist = [float('inf')] * (n + 1) 9 | queue = deque() 10 | queue.append(1) 11 | dist[1] = 0 12 | while queue: 13 | now = queue.popleft() 14 | for v in adj[now]: 15 | if dist[v] == float('inf'): 16 | queue.append(v) 17 | dist[v] = dist[now] + 1 18 | else: 19 | if (dist[now] - dist[v]) % 2 == 0: 20 | return 0 21 | return 1 22 | 23 | 24 | if __name__ == '__main__': 25 | n_vertices, n_edges = map(int, input().split()) 26 | adjacency_list = [[] for _ in range(n_vertices + 1)] 27 | for i in range(n_edges): 28 | a, b = map(int, input().split()) 29 | adjacency_list[a].append(b) 30 | adjacency_list[b].append(a) 31 | is_bipartite = isBipartite(n_vertices, adjacency_list) 32 | print(is_bipartite) 33 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week4-Fastest Route/1-Dijkstra_min-heap.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | # edges[a]: [(end, weight), ...] 8 | # H = [[node1, distance1], [node2, distance2],...] 9 | class MinimumCost: 10 | def __init__(self, n, edges, start, end): 11 | self.edges = edges 12 | self.H = deque() 13 | for i in range(n): 14 | self.H.append([i + 1, float('inf')]) 15 | self.dist = [float('inf')] * (n + 1) 16 | self.processed = [False] * (n + 1) 17 | self.start = start 18 | self.end = end 19 | 20 | def SiftDown(self, i): 21 | min_index = i 22 | left_child = 2 * i + 1 23 | right_child = 2 * i + 2 24 | if left_child < len(self.H) and self.H[left_child][1] < self.H[min_index][1]: 25 | min_index = left_child 26 | if right_child < len(self.H) and self.H[right_child][1] < self.H[min_index][1]: 27 | min_index = right_child 28 | if min_index != i: 29 | self.H[i], self.H[min_index] = self.H[min_index], self.H[i] 30 | self.SiftDown(min_index) 31 | 32 | def SiftUp(self, i): 33 | while i > 0 and self.H[i][1] < self.H[(i - 1) // 2][1]: 34 | self.H[i], self.H[(i - 1) // 2] = self.H[(i - 1) // 2], self.H[i] 35 | i = (i - 1) // 2 36 | 37 | def _SiftUp(self, i): 38 | while i > 0: 39 | parent = (i - 1) // 2 40 | if self.H[i][1] < self.H[parent][1]: 41 | self.H[i], self.H[parent] = self.H[parent], self.H[i] 42 | i = parent 43 | 44 | def ExtractMin(self): 45 | result = self.H[0] 46 | self.H[0] = self.H[len(self.H) - 1] 47 | self.H.pop() 48 | self.SiftDown(0) 49 | return result 50 | 51 | def Insert(self, node, p): 52 | self.H.append([node, p]) 53 | self.SiftUp(len(self.H) - 1) 54 | 55 | def ChangePriority(self, i, new): 56 | self.H[i][1] = new 57 | self.SiftUp(i) 58 | 59 | # edges[a]: [(end, weight), ...] 60 | # H = [[node1, distance1], [node2, distance2],...] 61 | def Dijkstra(self): 62 | self.dist[self.start] = 0 63 | self.ChangePriority(self.start - 1, 0) 64 | while self.H: 65 | u, dist_u = self.ExtractMin() 66 | if not self.processed[u]: 67 | self.processed[u] = True 68 | for v, weight in self.edges[u]: 69 | if self.dist[v] > self.dist[u] + weight: 70 | self.dist[v] = self.dist[u] + weight 71 | self.Insert(v, self.dist[v]) 72 | if self.dist[self.end] != float('inf'): 73 | return self.dist[self.end] 74 | else: 75 | return -1 76 | 77 | 78 | if __name__ == '__main__': 79 | n_vertices, n_edges = map(int, input().split()) 80 | edges = [[] for _ in range(n_vertices + 1)] 81 | for i in range(n_edges): 82 | a, b, w = map(int, input().split()) 83 | edges[a].append((b, w)) 84 | s, e = map(int, input().split()) 85 | flights = MinimumCost(n_vertices, edges, s, e) 86 | min_cost = flights.Dijkstra() 87 | print(min_cost) 88 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week4-Fastest Route/2-negative cycle.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | from collections import namedtuple 4 | 5 | # Edge = namedtuple('Edge', ['start', 'end', 'weight']) 6 | 7 | 8 | def BellmanFord(n, graph): 9 | # dist = [float('inf')] * (n + 1) 10 | dist = [1001] * (n + 1) 11 | dist[1] = 0 12 | prev = [None] * (n + 1) 13 | negative_nodes = [] 14 | for i in range(n): 15 | for u, v, w in graph: 16 | if dist[v] > dist[u] + w: 17 | dist[v] = dist[u] + w 18 | prev[v] = u 19 | if i == n - 1: 20 | negative_nodes.append(v) 21 | if not negative_nodes: 22 | return 0 23 | else: 24 | return 1 25 | 26 | 27 | if __name__ == '__main__': 28 | n_vertices, n_edges = map(int, input().split()) 29 | edges = [] 30 | for i in range(n_edges): 31 | a, b, w = map(int, input().split()) 32 | edges.append((a, b, w)) # (start, end, weight) 33 | negative_cycle = BellmanFord(n_vertices, edges) 34 | print(negative_cycle) 35 | 36 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week4-Fastest Route/3-infinite arbitrage.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | from collections import deque 4 | 5 | 6 | def BellmanFord(n, graph, adj, s): 7 | dist = [float('inf')] * (n + 1) 8 | dist[s] = 0 9 | prev = [None] * (n + 1) 10 | negative_nodes = deque() 11 | for i in range(n): 12 | for u, v, w in graph: 13 | if dist[v] > dist[u] + w: 14 | dist[v] = dist[u] + w 15 | prev[v] = u 16 | if i == n - 1: 17 | negative_nodes.append(v) 18 | 19 | visited = [False] * (n + 1) 20 | while negative_nodes: 21 | u = negative_nodes.popleft() 22 | visited[u] = True 23 | dist[u] = '-' # the mark of nodes reachable from negative cycle 24 | for v in adj[u]: 25 | if not visited[v]: 26 | negative_nodes.append(v) 27 | return dist 28 | 29 | 30 | if __name__ == '__main__': 31 | n_vertices, n_edges = map(int, input().split()) 32 | edges = [] 33 | adjacency_list = [[] for _ in range(n_vertices + 1)] 34 | for i in range(n_edges): 35 | a, b, w = map(int, input().split()) 36 | edges.append((a, b, w)) # (start, end, weight) 37 | adjacency_list[a].append(b) # start : [end, weight] 38 | start = int(input()) 39 | distance = BellmanFord(n_vertices, edges, adjacency_list, start) 40 | for dist in distance[1:]: 41 | if dist == float('inf'): 42 | print('*') 43 | else: 44 | print(dist) 45 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week5-Minimum Spanning Trees/1-connecting points.py: -------------------------------------------------------------------------------- 1 | # python 3 2 | 3 | import math 4 | 5 | 6 | class MinimumLength: 7 | def __init__(self, n, edges): 8 | self.parent = [i for i in range(n)] 9 | self.rank = [0] * n 10 | self.edges = edges 11 | 12 | def Find(self, i): 13 | if i != self.parent[i]: 14 | self.parent[i] = self.Find(self.parent[i]) 15 | return self.parent[i] 16 | 17 | def Union(self, i, j): 18 | i_parent = self.Find(i) 19 | j_parent = self.Find(j) 20 | if i_parent == j_parent: 21 | return 22 | else: 23 | if self.rank[i_parent] > self.rank[j_parent]: 24 | self.parent[j_parent] = i_parent 25 | else: 26 | self.parent[i_parent] = j_parent 27 | if self.rank[i_parent] == self.rank[j_parent]: 28 | self.rank[j_parent] += 1 29 | 30 | def Kruskal(self): 31 | dist = 0 32 | self.edges.sort(key=lambda i: i[2]) 33 | for u, v, w in edges: 34 | if self.Find(u) != self.Find(v): 35 | dist += w 36 | self.Union(u, v) 37 | return dist 38 | 39 | 40 | if __name__ == '__main__': 41 | n_vertices = int(input()) 42 | points = [None] * n_vertices # 0-based index 43 | for i in range(n_vertices): 44 | a, b = map(int, input().split()) 45 | points[i] = (a, b) 46 | edges = [] # (start, end, weight) 47 | 48 | for i in range(n_vertices): 49 | (x0, y0) = points[i] 50 | for j in range(i + 1, n_vertices): 51 | (x, y) = points[j] 52 | distance = math.sqrt((x - x0) ** 2 + (y - y0) ** 2) 53 | edges.append((i, j, distance)) 54 | graph = MinimumLength(n_vertices, edges) 55 | min_length = graph.Kruskal() 56 | print("{0: .9f}".format(min_length)) 57 | 58 | -------------------------------------------------------------------------------- /3-Algorithms on Graphs/Week5-Minimum Spanning Trees/2-clustering.py: -------------------------------------------------------------------------------- 1 | # python 3 2 | 3 | import math 4 | 5 | 6 | class MinimumLength: 7 | def __init__(self, n, edges, k): 8 | self.parent = [i for i in range(n)] 9 | self.rank = [0] * n 10 | self.edges = edges 11 | self.n = n 12 | self.k = k 13 | 14 | def Find(self, i): 15 | if i != self.parent[i]: 16 | self.parent[i] = self.Find(self.parent[i]) 17 | return self.parent[i] 18 | 19 | def Union(self, i, j): 20 | i_parent = self.Find(i) 21 | j_parent = self.Find(j) 22 | if i_parent == j_parent: 23 | return 24 | else: 25 | if self.rank[i_parent] > self.rank[j_parent]: 26 | self.parent[j_parent] = i_parent 27 | else: 28 | self.parent[i_parent] = j_parent 29 | if self.rank[i_parent] == self.rank[j_parent]: 30 | self.rank[j_parent] += 1 31 | 32 | def Kruskal(self): 33 | n_edge = 0 34 | self.edges.sort(key=lambda i: i[2]) 35 | for u, v, w in edges: 36 | if self.Find(u) != self.Find(v): 37 | if n_edge == self.n - self.k: 38 | return w 39 | else: 40 | n_edge += 1 41 | self.Union(u, v) 42 | 43 | 44 | if __name__ == '__main__': 45 | n_vertices = int(input()) 46 | points = [None] * n_vertices # 0-based index 47 | for i in range(n_vertices): 48 | a, b = map(int, input().split()) 49 | points[i] = (a, b) 50 | edges = [] # (start, end, weight) 51 | n_subsets = int(input()) 52 | for i in range(n_vertices): 53 | (x0, y0) = points[i] 54 | for j in range(i + 1, n_vertices): 55 | (x, y) = points[j] 56 | distance = math.sqrt((x - x0) ** 2 + (y - y0) ** 2) 57 | edges.append((i, j, distance)) 58 | graph = MinimumLength(n_vertices, edges, n_subsets) 59 | min_d = graph.Kruskal() 60 | print("{0: .9f}".format(min_d)) 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week1-Suffix Trees/1-constrcut a Trie from patterns.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | # tree = { node1: { label1: c1, label2: c2, ...}, node2, ... } 5 | def BuildTrie(patterns): 6 | tree = {} 7 | new_node = 0 8 | for pattern in patterns: 9 | current_node = 0 10 | for i in range(len(pattern)): 11 | current_symbol = pattern[i] 12 | if tree.__contains__(current_node) and tree[current_node].__contains__(current_symbol): 13 | current_node = tree[current_node].get(current_symbol) 14 | else: 15 | new_node = new_node + 1 16 | if not tree.__contains__(current_node): 17 | tree[current_node] = {} 18 | tree[current_node][current_symbol] = new_node 19 | else: 20 | tree[current_node][current_symbol] = new_node 21 | current_node = new_node 22 | return tree 23 | 24 | 25 | if __name__ == '__main__': 26 | n_patterns = int(input()) 27 | patterns = list(input() for _ in range(n_patterns)) 28 | tree = BuildTrie(patterns) 29 | for node in tree: 30 | for c in tree[node]: 31 | print("{}->{}:{}".format(node, tree[node][c], c)) 32 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week1-Suffix Trees/2-TrieMatching.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | 5 | def PrefixTrieMatching(text, trie): 6 | v = 0 7 | for i in range(len(text)): 8 | symbol = text[i] 9 | if trie[v].__contains__(symbol): 10 | v = trie[v][symbol] 11 | if not trie.__contains__(v): # v is a leaf in Trie 12 | return True 13 | else: 14 | return False 15 | 16 | 17 | def TrieMatching(text, trie): 18 | positions = [] 19 | n = len(text) 20 | for k in range(n): 21 | check = PrefixTrieMatching(text[k:], trie) 22 | if check: 23 | positions.append(k) 24 | return positions 25 | 26 | 27 | def BuildTrie(patterns): 28 | tree = {} 29 | new_node = 0 30 | for pattern in patterns: 31 | current_node = 0 32 | for i in range(len(pattern)): 33 | current_symbol = pattern[i] 34 | if tree.__contains__(current_node) and tree[current_node].__contains__(current_symbol): 35 | current_node = tree[current_node].get(current_symbol) 36 | else: 37 | new_node += 1 38 | if not tree.__contains__(current_node): 39 | tree[current_node] = {} 40 | tree[current_node][current_symbol] = new_node 41 | else: 42 | tree[current_node][current_symbol] = new_node 43 | current_node = new_node 44 | return tree 45 | 46 | 47 | if __name__ == '__main__': 48 | text = input() 49 | n_patterns = int(input()) 50 | patterns = list(input() for _ in range(n_patterns)) 51 | tree = BuildTrie(patterns) 52 | result = TrieMatching(text, tree) 53 | for pos in result: 54 | print(pos, end=' ') 55 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week1-Suffix Trees/3-Extend TrieMatching.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def PrefixTrieMatching(text, trie): 5 | v = 0 6 | i = 0 7 | for i in range(len(text)): 8 | symbol = text[i] 9 | if trie[v].__contains__(symbol): 10 | v = trie[v][symbol] 11 | if trie[v].__contains__('$'): # v is a leaf in Trie 12 | return True 13 | else: 14 | return False 15 | 16 | 17 | def TrieMatching(text, trie): 18 | positions = [] 19 | n = len(text) 20 | for k in range(n): 21 | check = PrefixTrieMatching(text[k:], trie) 22 | if check: 23 | positions.append(k) 24 | return positions 25 | 26 | 27 | def BuildTrie(patterns): 28 | tree = {} 29 | new_node = 0 30 | for pattern in patterns: 31 | current_node = 0 32 | for i in range(len(pattern)): 33 | current_symbol = pattern[i] 34 | if tree.__contains__(current_node) and tree[current_node].__contains__(current_symbol): 35 | current_node = tree[current_node].get(current_symbol) 36 | else: 37 | new_node += 1 38 | if not tree.__contains__(current_node): 39 | tree[current_node] = {} 40 | tree[current_node][current_symbol] = new_node 41 | else: 42 | tree[current_node][current_symbol] = new_node 43 | current_node = new_node 44 | return tree 45 | 46 | 47 | if __name__ == '__main__': 48 | text = input() + '$' 49 | n_patterns = int(input()) 50 | patterns = list(input() + '$' for _ in range(n_patterns)) 51 | tree = BuildTrie(patterns) 52 | result = TrieMatching(text, tree) 53 | for pos in result: 54 | print(pos, end=' ') 55 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week1-Suffix Trees/4-construct the suffix tree of a string.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | class SuffixTree(object): 7 | 8 | class Node(object): 9 | def __init__(self, lab): 10 | self.lab = lab # label on path leading to this node 11 | self.out = {} # outgoing edges; maps characters to nodes 12 | 13 | def __init__(self, s): 14 | """ Make suffix tree, without suffix links, from s in quadratic time 15 | and linear space """ 16 | self.root = self.Node(None) 17 | self.root.out[s[0]] = self.Node(s) # trie for just longest suf 18 | # add the rest of the suffixes, from longest to shortest 19 | for i in range(1, len(s)): 20 | # start at root; we’ll walk down as far as we can go 21 | cur = self.root 22 | j = i 23 | while j < len(s): 24 | if s[j] in cur.out: 25 | child = cur.out[s[j]] 26 | lab = child.lab 27 | # Walk along edge until we exhaust edge label or 28 | # until we mismatch 29 | k = j+1 30 | # print('j', j, 'K',k, 'label', child.lab) 31 | while k-j < len(lab) and s[k] == lab[k-j]: 32 | k += 1 33 | if k-j == len(lab): 34 | cur = child # we exhausted the edge 35 | j = k 36 | else: 37 | # we fell off in middle of edge 38 | cExist, cNew = lab[k-j], s[k] 39 | # create “mid”: new node bisecting edge 40 | mid = self.Node(lab[:k-j]) 41 | mid.out[cNew] = self.Node(s[k:]) 42 | # original child becomes mid’s child 43 | child.lab = lab[k-j:] 44 | mid.out[cExist] = child 45 | # original child’s label is curtailed 46 | # print('j-', j, 'label', child.lab) 47 | # mid becomes new child of original parent 48 | cur.out[s[j]] = mid 49 | else: 50 | # Fell off tree at a node: make new edge hanging off it 51 | cur.out[s[j]] = self.Node(s[j:]) 52 | 53 | def Print(self): 54 | queue = deque() 55 | queue.append(self.root) 56 | while queue: 57 | u = queue.popleft() 58 | if u != self.root: 59 | print(u.lab) 60 | for label, node in u.out.items(): 61 | queue.append(node) 62 | 63 | 64 | if __name__ =='__main__': 65 | text = input() 66 | stree = SuffixTree(text) 67 | stree.Print() 68 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week1-Suffix Trees/5-shortest non-shared substring.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | import sys 4 | import threading 5 | 6 | sys.setrecursionlimit(10**7) # max depth of recursion 7 | threading.stack_size(2**25) # new thread will get stack of such size 8 | 9 | 10 | class SuffixTree: 11 | 12 | class Node: 13 | def __init__(self,label): 14 | self.label = label 15 | self.out = {} 16 | self.type = 'L' 17 | self.visited = False 18 | self.parent = None 19 | 20 | def __init__(self,s): 21 | self.root = self.Node(None) 22 | self.root.out[s[0]] = self.Node(s) 23 | for i in range(1, len(s)): 24 | j = i 25 | cur = self.root 26 | while j < len(s): 27 | if s[j] in cur.out: 28 | child = cur.out[s[j]] 29 | label = child.label 30 | k = j + 1 31 | while k - j < len(label) and s[k] == label[k-j]: 32 | k += 1 33 | if k - j == len(label): 34 | j = k 35 | cur = child 36 | else: 37 | cExist, cNew = label[k-j], s[k] 38 | mid = self.Node(label[:k-j]) 39 | mid.out[cNew] = self.Node(s[k:]) 40 | child.label = label[k-j:] 41 | mid.out[cExist] = child # cExist无需新建结点,续用原来的结点即可 42 | cur.out[s[j]] = mid 43 | else: 44 | cur.out[s[j]] = self.Node(s[j:]) 45 | 46 | def Explore(self, cur, Lleaves): # depth-first search in DAG 47 | cur.visited = True 48 | if len(cur.out) == 0: 49 | if '#' not in cur.label: 50 | cur.type = 'R' 51 | else: 52 | Lleaves.append(cur) 53 | else: 54 | for a, node in cur.out.items(): 55 | if not node.visited: 56 | node.parent = cur 57 | self.Explore(node, Lleaves) 58 | for a, node in cur.out.items(): 59 | if node.type == 'R': 60 | cur.type = 'R' 61 | 62 | def ShortestUncommonString(self): 63 | Lleaves = [] 64 | self.Explore(self.root, Lleaves) 65 | results = [] 66 | for leaf in Lleaves: 67 | char = '' 68 | substring = '' 69 | cur = leaf.parent 70 | if leaf.label[0] == '#' and cur.type == 'R': 71 | continue 72 | elif cur.type == 'R': 73 | char += leaf.label[0] 74 | while cur != self.root: 75 | substring = cur.label + substring 76 | cur = cur.parent 77 | substring += char 78 | results.append(substring) 79 | result = min(results, key=lambda x:len(x)) 80 | return result 81 | 82 | 83 | def main(): 84 | s = input() 85 | t = input() 86 | text = s + '#' + t + '$' 87 | suffix_tree = SuffixTree(text) 88 | result = suffix_tree.ShortestUncommonString() 89 | print(result) 90 | 91 | 92 | threading.Thread(target=main).start() 93 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week2-Burrows–Wheeler Transform and Suffix Arrays/1-construct BWT.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def BWT(s): 5 | matrix = [s] 6 | for i in range(1, len(s)): 7 | s = s[-1] + s[:-1] 8 | matrix.append(s) 9 | matrix.sort() 10 | bwt = '' 11 | for t in matrix: 12 | bwt += t[-1] 13 | return bwt 14 | 15 | 16 | if __name__ == '__main__': 17 | text = input() 18 | print(BWT(text)) 19 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week2-Burrows–Wheeler Transform and Suffix Arrays/2-invert BWT.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def LastToFirst(s): 5 | counts = {'$': 0, "A": 0, 'C': 0, 'G': 0, 'T': 0} 6 | for char in s: 7 | counts[char] += 1 8 | temp = -1 9 | position = {} 10 | for t in ['$', 'A', 'C', 'G', 'T']: 11 | temp += counts[t] 12 | position[t] = temp 13 | array = [0] * len(s) 14 | for i in range(len(s)-1, -1, -1): 15 | array[i] = position[s[i]] 16 | position[s[i]] -= 1 17 | return array 18 | 19 | 20 | def invertBWT(s): 21 | last_to_first = LastToFirst(s) 22 | result = '$' 23 | pos = 0 24 | for i in range(len(s) - 1): 25 | result += s[pos] 26 | pos = last_to_first[pos] 27 | result = result[::-1] 28 | return result 29 | 30 | 31 | if __name__ == '__main__': 32 | bwt = input() 33 | print(invertBWT(bwt)) 34 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week2-Burrows–Wheeler Transform and Suffix Arrays/3-BWmatching.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def Process(s): 5 | freq = {'$': 0, "A": 0, 'C': 0, 'G': 0, 'T': 0} 6 | for char in s: 7 | freq[char] += 1 8 | ele = ['$', 'A', 'C', 'G', 'T'] 9 | first_occur = {'$': 0} 10 | for i in range(1, 5): 11 | first_occur[ele[i]] = first_occur[ele[i-1]] + freq[ele[i-1]] 12 | count = {} 13 | for e in ele: 14 | count[e] = [0] * (len(s) + 1) 15 | for i in range(len(s)): 16 | temp = {s[i]: 1} 17 | for e in ele: 18 | count[e][i+1] = count[e][i] + temp.get(e, 0) 19 | return first_occur, count 20 | 21 | 22 | def BWMatching(s, p, first_occur, count): 23 | top = 0 24 | bottom = len(s) - 1 25 | while top <= bottom: 26 | if p: 27 | symbol = p[-1] 28 | p = p[:-1] 29 | top = first_occur[symbol] + count[symbol][top] 30 | bottom = first_occur[symbol] + count[symbol][bottom + 1] - 1 31 | else: 32 | return bottom - top + 1 33 | return 0 # pattern not in string 34 | 35 | 36 | if __name__ == '__main__': 37 | bwt = input() 38 | n_patterns = int(input()) 39 | patterns = list(input().split()) 40 | first_occur, count = Process(bwt) 41 | for pattern in patterns: 42 | result = BWMatching(bwt, pattern, first_occur, count) 43 | print(result, end=' ') 44 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week2-Burrows–Wheeler Transform and Suffix Arrays/4-suffix array.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def SuffixArray(s): 5 | suffix = [] 6 | for i in range(len(s)): 7 | suffix.append((s[i:], i)) # (suffix, starting position) 8 | suffix.sort() 9 | result = [] 10 | for e in suffix: 11 | result.append(e[1]) 12 | return result 13 | 14 | 15 | if __name__ == '__main__': 16 | text = input() 17 | suffix_array = SuffixArray(text) 18 | for e in suffix_array: 19 | print(e, end=' ') 20 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week4-Algorithmic Challenges/1-Knuth Morris Pratt.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def PrefixFunction(p): 5 | s = [0] * len(p) 6 | border = 0 7 | for i in range(1, len(p)): 8 | while border > 0 and p[i] != p[border]: 9 | border = s[border - 1] 10 | if p[i] == p[border]: 11 | border += 1 12 | else: 13 | border = 0 14 | s[i] = border 15 | return s 16 | 17 | 18 | def KMP(p, t): 19 | s = p + '$' + t 20 | pre_func = PrefixFunction(s) 21 | result = [] 22 | for i in range(len(p) + 1, len(s)): 23 | if pre_func[i] == len(p): 24 | result.append(i - 2 * len(p)) 25 | return result 26 | 27 | 28 | if __name__ == '__main__': 29 | pattern = input() 30 | text = input() 31 | positions = KMP(pattern, text) 32 | for pos in positions: 33 | print(pos, end=' ') 34 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week4-Algorithmic Challenges/2-suffix array for a long string.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def SortCharacters(s): 5 | order = [0] * len(s) 6 | count = {'$': 0, "A": 0, 'C': 0, 'G': 0, 'T': 0} 7 | for char in s: 8 | count[char] += 1 9 | ele = ['$', 'A', 'C', 'G', 'T'] 10 | for i in range(1, 5): 11 | count[ele[i]] += count[ele[i-1]] 12 | for j in range(len(s) - 1, -1, -1): 13 | c = s[j] 14 | count[c] -= 1 15 | order[count[c]] = j 16 | return order 17 | 18 | 19 | def ComputeCharClasses(s, order): 20 | char_class = [0] * len(s) 21 | for i in range(1, len(s)): 22 | if s[order[i]] == s[order[i-1]]: 23 | char_class[order[i]] = char_class[order[i-1]] 24 | else: 25 | char_class[order[i]] = char_class[order[i-1]] + 1 26 | return char_class 27 | 28 | 29 | def SortDoubled(s, L, old_order, old_class): 30 | count = [0] * len(s) 31 | new_order = [0] * len(s) 32 | for i in range(len(s)): 33 | count[old_class[i]] += 1 34 | for i in range(1, len(s)): 35 | count[i] += count[i-1] 36 | for j in range(len(s) - 1, -1, -1): 37 | start = (old_order[j] - L + len(s)) % len(s) 38 | cl = old_class[start] 39 | count[cl] -= 1 40 | new_order[count[cl]] = start 41 | return new_order 42 | 43 | 44 | def UpdateClasses(new_order, old_class, L): 45 | n = len(new_order) 46 | new_class = [0] * n 47 | for i in range(1, n): 48 | cur = new_order[i] 49 | mid = (cur + L) % n 50 | prev = new_order[i-1] 51 | mid_prev = (prev + L) % n 52 | if old_class[cur] == old_class[prev] and old_class[mid] == old_class[mid_prev]: 53 | new_class[cur] = new_class[prev] 54 | else: 55 | new_class[cur] = new_class[prev] + 1 56 | return new_class 57 | 58 | 59 | def BuildSuffixArray(s): 60 | order = SortCharacters(s) 61 | _class = ComputeCharClasses(s, order) 62 | L = 1 63 | while L < len(s): 64 | order = SortDoubled(s, L, order, _class) 65 | _class = UpdateClasses(order, _class, L) 66 | L = 2 * L 67 | return order 68 | 69 | 70 | if __name__ == '__main__': 71 | text = input() 72 | suffix_array = BuildSuffixArray(text) 73 | for e in suffix_array: 74 | print(e, end=' ') 75 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week4-Algorithmic Challenges/3-suffix array matching.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def SortCharacters(s): 5 | order = [0] * len(s) 6 | count = {'$': 0, "A": 0, 'C': 0, 'G': 0, 'T': 0} 7 | for char in s: 8 | count[char] += 1 9 | ele = ['$', 'A', 'C', 'G', 'T'] 10 | for i in range(1, 5): 11 | count[ele[i]] += count[ele[i-1]] 12 | for j in range(len(s) - 1, -1, -1): 13 | c = s[j] 14 | count[c] -= 1 15 | order[count[c]] = j 16 | return order 17 | 18 | 19 | def ComputeCharClasses(s, order): 20 | char_class = [0] * len(s) 21 | for i in range(1, len(s)): 22 | if s[order[i]] == s[order[i-1]]: 23 | char_class[order[i]] = char_class[order[i-1]] 24 | else: 25 | char_class[order[i]] = char_class[order[i-1]] + 1 26 | return char_class 27 | 28 | 29 | def SortDoubled(s, L, old_order, old_class): 30 | count = [0] * len(s) 31 | new_order = [0] * len(s) 32 | for i in range(len(s)): 33 | count[old_class[i]] += 1 34 | for i in range(1, len(s)): 35 | count[i] += count[i-1] 36 | for j in range(len(s) - 1, -1, -1): 37 | start = (old_order[j] - L + len(s)) % len(s) 38 | cl = old_class[start] 39 | count[cl] -= 1 40 | new_order[count[cl]] = start 41 | return new_order 42 | 43 | 44 | def UpdateClasses(new_order, old_class, L): 45 | n = len(new_order) 46 | new_class = [0] * n 47 | for i in range(1, n): 48 | cur = new_order[i] 49 | mid = (cur + L) % n 50 | prev = new_order[i-1] 51 | mid_prev = (prev + L) % n 52 | if old_class[cur] == old_class[prev] and old_class[mid] == old_class[mid_prev]: 53 | new_class[cur] = new_class[prev] 54 | else: 55 | new_class[cur] = new_class[prev] + 1 56 | return new_class 57 | 58 | 59 | def BuildSuffixArray(s): 60 | order = SortCharacters(s) 61 | _class = ComputeCharClasses(s, order) 62 | L = 1 63 | while L < len(s): 64 | order = SortDoubled(s, L, order, _class) 65 | _class = UpdateClasses(order, _class, L) 66 | L = 2 * L 67 | return order[1:] 68 | 69 | 70 | def PatternMatchingWithSuffixArray(t, p, sa): 71 | # print(sa) 72 | min = 0 73 | max = len(t) 74 | while min < max: 75 | mid = (min + max) // 2 76 | # print(min, max, mid) 77 | suffix = sa[mid] 78 | i = 0 79 | while i < len(p) and suffix + i < len(t): 80 | if p[i] > t[suffix + i]: 81 | min = mid + 1 82 | break 83 | elif p[i] < t[suffix + i]: 84 | max = mid 85 | break 86 | i += 1 87 | if i == len(p): 88 | max = mid 89 | elif suffix + i == len(t): 90 | min = mid + 1 91 | start = min 92 | # print(start) 93 | max = len(t) 94 | while min < max: 95 | mid = (min + max) // 2 96 | # print(min, max, mid) 97 | suffix = sa[mid] 98 | i = 0 99 | while i < len(p) and suffix + i < len(t): 100 | if p[i] < t[suffix + i]: 101 | max = mid 102 | break 103 | i += 1 104 | if i == len(p) and i <= len(t) - suffix: # if p is a prefix of the suffix, we treat p >= suffix 105 | min = mid + 1 106 | end = max - 1 107 | return start, end 108 | 109 | 110 | if __name__ == '__main__': 111 | text = input() 112 | n_patterns = int(input()) 113 | patterns = list(input().split()) 114 | suffix_array = BuildSuffixArray(text+'$') 115 | result = [0] * len(text) 116 | for pattern in patterns: 117 | s, e = PatternMatchingWithSuffixArray(text, pattern, suffix_array) 118 | if s <= e: 119 | for i in range(s, e + 1): 120 | pos = suffix_array[i] 121 | if result[pos] == 0: 122 | print(pos, end=' ') 123 | result[pos] += 1 124 | -------------------------------------------------------------------------------- /4-Algorithms on Strings/Week4-Algorithmic Challenges/4-suffix tree from array.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | import sys 5 | import threading 6 | 7 | sys.setrecursionlimit(10 ** 7) # max depth of recursion 8 | threading.stack_size(2 ** 27) # new thread will get stack of such size 9 | 10 | 11 | class SuffixTree: 12 | class Node: 13 | def __init__(self, node, depth, start, end): 14 | self.parent = node 15 | self.children = {} 16 | self.depth = depth # string depth 17 | self.start = start 18 | self.end = end 19 | self.visited = False 20 | 21 | def __init__(self, s, order, LCP): 22 | self.s = s 23 | self.ele = ['$', 'A', 'C', 'G', 'T'] 24 | self.order = order 25 | self.LCP = LCP 26 | self.root = self.Node(None, 0, -1, -1) 27 | 28 | def CreateNewLeaf(self, node, suffix): 29 | leaf = self.Node(node, len(self.s) - suffix, suffix + node.depth, len(self.s)) 30 | node.children[self.s[leaf.start]] = leaf 31 | return leaf 32 | 33 | def BreakEdge(self, node, mid_start, offset): 34 | mid_char = self.s[mid_start] 35 | left_char = self.s[mid_start + offset] 36 | mid = self.Node(node, node.depth + offset, mid_start, mid_start + offset) 37 | mid.children[left_char] = node.children[mid_char] 38 | node.children[mid_char].parent = mid 39 | node.children[mid_char].start += offset 40 | node.children[mid_char] = mid 41 | return mid 42 | 43 | def STFromSA(self): 44 | lcp_prev = 0 45 | cur = self.root 46 | for i in range(len(self.s)): 47 | suffix = self.order[i] 48 | while cur.depth > lcp_prev: 49 | cur = cur.parent 50 | if cur.depth == lcp_prev: 51 | cur = self.CreateNewLeaf(cur, suffix) 52 | else: 53 | # break edge and got 3 nodes: mid, left(exist already), right(new suffix) 54 | mid_start = self.order[i - 1] + cur.depth # the start of mid-node 55 | offset = lcp_prev - cur.depth # the number of characters of mid-node 56 | mid = self.BreakEdge(cur, mid_start, offset) 57 | cur = self.CreateNewLeaf(mid, suffix) 58 | if i < len(self.s) - 1: 59 | lcp_prev = self.LCP[i] 60 | 61 | def PrintEdges(self, cur): 62 | cur.visited = True 63 | if cur != self.root: 64 | print(cur.start, cur.end) 65 | for i in range(5): 66 | child = cur.children.get(self.ele[i], None) 67 | if child is not None and not child.visited: 68 | self.PrintEdges(child) 69 | 70 | 71 | def main(): 72 | text = input() 73 | suffix_array = list(map(int, input().split())) 74 | lcp = list(map(int, input().split())) 75 | print(text) 76 | suffix_tree = SuffixTree(text, suffix_array, lcp) 77 | suffix_tree.STFromSA() 78 | suffix_tree.PrintEdges(suffix_tree.root) 79 | 80 | 81 | threading.Thread(target=main).start() 82 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week1-Flows in Networks/1-max flow.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | def HasPath(Gf, path): 8 | n = len(Gf) 9 | visited = [False] * n 10 | visited[0] = True 11 | queue = deque([0]) 12 | while queue: 13 | temp = queue.popleft() 14 | if temp == n - 1: 15 | return True 16 | for i in range(n): 17 | if not visited[i] and Gf[temp][i] > 0: 18 | queue.append(i) 19 | visited[i] = True 20 | path[i] = temp 21 | return visited[n-1] 22 | 23 | 24 | def MaxFlow(Gf): 25 | flow = 0 26 | n = len(Gf) 27 | path = list(range(n)) 28 | while HasPath(Gf, path): 29 | min_flow = float('inf') 30 | # find cf(p) 31 | v = n - 1 32 | while v != 0: 33 | u = path[v] 34 | min_flow = min(min_flow, Gf[u][v]) 35 | v = u 36 | # add flow in every edge of the augument path 37 | v = n - 1 38 | while v != 0: 39 | u = path[v] 40 | Gf[u][v] -= min_flow 41 | Gf[v][u] += min_flow 42 | v = u 43 | flow += min_flow 44 | return flow 45 | 46 | 47 | if __name__ == '__main__': 48 | n_city, n_edges = map(int, input().split()) 49 | residual_graph = [[0] * n_city for i in range(n_city)] 50 | for _ in range(n_edges): 51 | u, v, capacity = map(int, input().split()) 52 | residual_graph[u - 1][v - 1] += capacity 53 | print(residual_graph) 54 | max_flow = MaxFlow(residual_graph) 55 | print(max_flow) 56 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week1-Flows in Networks/2-bipartite matching.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | def MakeNetwork(n, m, bip): 8 | graph = [[0] * (n + m + 2) for _ in range(n + m + 2)] 9 | for i in range(1, n + 1): 10 | graph[0][i] = 1 11 | for j in range(m): 12 | graph[i][n + 1 + j] = bip[i - 1][j] 13 | for k in range(n + 1, n + m + 1): 14 | graph[k][-1] = 1 15 | return graph 16 | 17 | 18 | def HasPath(Gf, path): 19 | V = len(Gf) 20 | visited = [False] * V 21 | visited[0] = True 22 | queue = deque([0]) 23 | while queue: 24 | u = queue.popleft() 25 | if u == V - 1: 26 | return True 27 | for v in range(V): 28 | if not visited[v] and Gf[u][v] > 0: 29 | queue.append(v) 30 | visited[v] = True 31 | path[v] = u 32 | return visited[V - 1] 33 | 34 | 35 | def MaxFlow(Gf, n): 36 | V = len(Gf) 37 | path = list(range(V)) 38 | while HasPath(Gf, path): 39 | min_flow = float('inf') 40 | v = V - 1 41 | while v != 0: 42 | u = path[v] 43 | min_flow = min(min_flow, Gf[u][v]) 44 | v = u 45 | v = V - 1 46 | while v != 0: 47 | u = path[v] 48 | Gf[u][v] -= min_flow 49 | Gf[v][u] += min_flow 50 | v = u 51 | matches = [-1] * n 52 | for i in range(V): 53 | if Gf[V-1][i] == 1: 54 | person = i - n 55 | flight = Gf[i].index(1) 56 | matches[flight - 1] = person 57 | return matches 58 | 59 | 60 | if __name__ == '__main__': 61 | n_flights, n_crew = map(int, input().split()) 62 | bipartite = [list(map(int, input().split())) for i in range(n_flights)] 63 | 64 | residual_graph = MakeNetwork(n_flights, n_crew, bipartite) 65 | matching = MaxFlow(residual_graph, n_flights) 66 | 67 | for e in matching: 68 | print(e, end=' ') 69 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week1-Flows in Networks/3-stock charts.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from collections import deque 5 | 6 | 7 | def DAG(n, stock): 8 | adj = [[0] * n for _ in range(n)] 9 | for i in range(n): 10 | for j in range(i + 1, n): 11 | above = all([x < y for x, y in zip(stock[i], stock[j])]) 12 | below = all([x > y for x, y in zip(stock[i], stock[j])]) 13 | if above: 14 | adj[i][j] = 1 15 | elif below: 16 | adj[j][i] = 1 17 | return adj 18 | 19 | 20 | def MakeNetwork(n, adj): 21 | graph = [[0] * (2 * n + 2) for _ in range(2 * n + 2)] 22 | for i in range(1, n + 1): 23 | graph[0][i] = 1 24 | for j in range(n): 25 | graph[i][n + 1 + j] = adj[i - 1][j] 26 | for k in range(n + 1, 2 * n + 1): 27 | graph[k][-1] = 1 28 | return graph 29 | 30 | 31 | def HasPath(Gf, path): 32 | k = len(Gf) # network中一共k个结点 33 | visited = [False] * k 34 | queue = deque([0]) 35 | visited[0] = True 36 | while queue: 37 | u = queue.pop() 38 | if u == k - 1: 39 | return True 40 | for v in range(k): 41 | if not visited[v] and graph[u][v] > 0: 42 | queue.append(v) 43 | visited[v] = True 44 | path[v] = u 45 | return visited[-1] 46 | 47 | 48 | def MaxFlow(Gf): 49 | k = len(Gf) 50 | path = [-1] * k 51 | flow = 0 52 | while HasPath(Gf, path): 53 | min_flow = float('inf') 54 | v = k - 1 55 | while v > 0: 56 | u = path[v] 57 | min_flow = min(min_flow, graph[u][v]) 58 | v = u 59 | v = k - 1 60 | while v > 0: 61 | u = path[v] 62 | graph[u][v] -= min_flow 63 | graph[v][u] += min_flow 64 | v = u 65 | flow += min_flow 66 | return flow 67 | 68 | 69 | if __name__ == '__main__': 70 | n, m = map(int, input().split()) 71 | stock_data = [list(map(int, input().split())) for _ in range(n)] 72 | adj_matrix = DAG(n, stock_data) 73 | graph = MakeNetwork(n, adj_matrix) 74 | max_flow = MaxFlow(graph) 75 | min_charts = n - max_flow 76 | print(min_charts) 77 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week2-Linear Programming/1-Gaussian Elimination.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | EPS = 1e-6 5 | PRECISION = 6 6 | 7 | class Equation: 8 | def __init__(self, a, b): 9 | self.a = a 10 | self.b = b 11 | 12 | class Position: 13 | def __init__(self, row, col): 14 | self.row = row 15 | self.col = col 16 | 17 | def ReadData(): 18 | size = int(input()) 19 | a = [] 20 | b = [] 21 | for _ in range(size): 22 | line = list(map(float, input().split())) 23 | a.append(line[:size]) 24 | b.append(line[size]) 25 | return Equation(a, b) 26 | 27 | def SelectPivotElement(pivot, a, used_rows): 28 | while used_rows[pivot.row] or a[pivot.row][pivot.col] == 0: 29 | pivot.row += 1 30 | return pivot 31 | 32 | # swap row to top of non-pivot rows 33 | def SwapLines(a, b, used_rows, pivot): 34 | a[pivot.col], a[pivot.row] = a[pivot.row], a[pivot.col] 35 | b[pivot.col], b[pivot.row] = b[pivot.row], b[pivot.col] 36 | used_rows[pivot.col], used_rows[pivot.row] = used_rows[pivot.row], used_rows[pivot.col] 37 | pivot.row = pivot.col 38 | 39 | def ProcessPivotElement(a, b, pivot, used_rows): 40 | scale = a[pivot.row][pivot.col] 41 | if scale != 1: 42 | for i in range(len(a)): 43 | a[pivot.row][i] /= scale 44 | b[pivot.row] /= scale 45 | for i in range(len(a)): 46 | if i != pivot.row: 47 | multiple = a[i][pivot.col] 48 | for j in range(len(a)): 49 | a[i][j] -= a[pivot.row][j] * multiple 50 | b[i] -= b[pivot.row] * multiple 51 | used_rows[pivot.row] = True 52 | 53 | 54 | def SolveEquation(equation): 55 | a = equation.a 56 | b = equation.b 57 | size = len(a) 58 | used_rows = [False] * size 59 | for i in range(size): 60 | pivot = Position(0, i) 61 | pivot = SelectPivotElement(pivot, a, used_rows) 62 | print(pivot.row, pivot.col) 63 | SwapLines(a, b, used_rows, pivot) 64 | ProcessPivotElement(a, b, pivot, used_rows) 65 | print(a, b) 66 | return b 67 | 68 | def PrintColumn(column): 69 | for e in column: 70 | print("{0:.6f}".format(e), end=' ') 71 | 72 | 73 | if __name__ == '__main__': 74 | matrix = ReadData() 75 | solution = SolveEquation(matrix) 76 | PrintColumn(solution) 77 | exit(0) 78 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week2-Linear Programming/1b.py: -------------------------------------------------------------------------------- 1 | # python3 2 | # 增加了无解和多解情况 3 | 4 | EPS = 1e-6 5 | PRECISION = 6 6 | 7 | class Equation: 8 | def __init__(self, a, b): 9 | self.a = a 10 | self.b = b 11 | 12 | class Position: 13 | def __init__(self, row, col): 14 | self.row = row 15 | self.col = col 16 | 17 | def ReadData(): 18 | size = int(input()) 19 | a = [] 20 | b = [] 21 | for _ in range(size): 22 | line = list(map(float, input().split())) 23 | a.append(line[:size]) 24 | b.append(line[size]) 25 | return Equation(a, b) 26 | 27 | def SelectPivotElement(pivot, a, used_rows): 28 | while pivot.row < len(a) and (used_rows[pivot.row] or a[pivot.row][pivot.col] == 0): 29 | pivot.row += 1 30 | print(pivot.row) 31 | if pivot.row == len(a): 32 | return False 33 | else: 34 | return pivot 35 | 36 | # swap row to top of non-pivot rows 37 | def SwapLines(a, b, used_rows, pivot): 38 | a[pivot.col], a[pivot.row] = a[pivot.row], a[pivot.col] 39 | b[pivot.col], b[pivot.row] = b[pivot.row], b[pivot.col] 40 | used_rows[pivot.col], used_rows[pivot.row] = used_rows[pivot.row], used_rows[pivot.col] 41 | pivot.row = pivot.col 42 | 43 | def ProcessPivotElement(a, b, pivot, used_rows): 44 | scale = a[pivot.row][pivot.col] 45 | if scale != 1: 46 | for i in range(len(a)): 47 | a[pivot.row][i] /= scale 48 | b[pivot.row] /= scale 49 | for i in range(len(a)): 50 | if i != pivot.row: 51 | multiple = a[i][pivot.col] 52 | for j in range(len(a)): 53 | a[i][j] -= a[pivot.row][j] * multiple 54 | b[i] -= b[pivot.row] * multiple 55 | used_rows[pivot.row] = True 56 | 57 | 58 | def SolveEquation(equation): 59 | a = equation.a 60 | b = equation.b 61 | size = len(a) 62 | used_rows = [False] * size 63 | for i in range(size): 64 | pivot = Position(0, i) 65 | pivot = SelectPivotElement(pivot, a, used_rows) 66 | if not pivot: 67 | return None 68 | else: 69 | print(pivot.row, pivot.col) 70 | SwapLines(a, b, used_rows, pivot) 71 | ProcessPivotElement(a, b, pivot, used_rows) 72 | print(a, b) 73 | return b 74 | 75 | def PrintColumn(column): 76 | for e in column: 77 | print("{0:.6f}".format(e), end=' ') 78 | 79 | 80 | if __name__ == '__main__': 81 | matrix = ReadData() 82 | solution = SolveEquation(matrix) 83 | PrintColumn(solution) 84 | exit(0) 85 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week2-Linear Programming/2-LP brute force.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | import itertools 5 | import copy 6 | 7 | EPS = 1e-18 8 | PRECISION = 18 9 | 10 | 11 | class Position: 12 | def __init__(self, row, col): 13 | self.row = row 14 | self.col = col 15 | 16 | def SelectPivotElement(pivot, a, used_rows): 17 | while pivot.row < len(a) and (used_rows[pivot.row] or a[pivot.row][pivot.col] == 0): 18 | pivot.row += 1 19 | if pivot.row == len(a): 20 | return False 21 | else: 22 | return pivot 23 | 24 | # swap row to top of non-pivot rows 25 | def SwapLines(a, b, used_rows, pivot): 26 | a[pivot.col], a[pivot.row] = a[pivot.row], a[pivot.col] 27 | b[pivot.col], b[pivot.row] = b[pivot.row], b[pivot.col] 28 | used_rows[pivot.col], used_rows[pivot.row] = used_rows[pivot.row], used_rows[pivot.col] 29 | pivot.row = pivot.col 30 | 31 | def ProcessPivotElement(a, b, pivot, used_rows): 32 | scale = a[pivot.row][pivot.col] 33 | if scale != 1: 34 | for i in range(len(a)): 35 | a[pivot.row][i] /= scale 36 | b[pivot.row] /= scale 37 | for i in range(len(a)): 38 | if i != pivot.row: 39 | multiple = a[i][pivot.col] 40 | for j in range(len(a)): 41 | a[i][j] -= a[pivot.row][j] * multiple 42 | b[i] -= b[pivot.row] * multiple 43 | used_rows[pivot.row] = True 44 | 45 | def FindSubsets(n, m): 46 | lst = list(range(n + m + 1)) 47 | subsets = list(map(set, itertools.combinations(lst, m))) 48 | return subsets 49 | 50 | def GaussianElimination(subset, A, B): 51 | # make equation 52 | a = [] 53 | b = [] 54 | for i in subset: 55 | a.append(copy.deepcopy(A[i])) 56 | b.append(copy.deepcopy(B[i])) 57 | # solve equation 58 | size = len(a) 59 | used_rows = [False] * size 60 | for i in range(size): 61 | pivot = Position(0, i) 62 | pivot = SelectPivotElement(pivot, a, used_rows) 63 | if not pivot: 64 | return None 65 | else: 66 | SwapLines(a, b, used_rows, pivot) 67 | ProcessPivotElement(a, b, pivot, used_rows) 68 | return b 69 | 70 | def CheckSolution(solution, A, B, m): 71 | for i in range(len(A)): 72 | sum = 0 73 | for j in range(m): 74 | sum += A[i][j] * solution[j] 75 | if sum - B[i] > 0.00001: 76 | return False 77 | return True 78 | 79 | def Solve(subsets, A, B, pleasure, m): 80 | solutions = [] 81 | for subset in subsets: 82 | solution = GaussianElimination(subset, A, B) 83 | if solution is not None: 84 | if CheckSolution(solution, A, B, m): 85 | solutions.append(solution) 86 | if len(solutions) == 0: 87 | print('No solution') 88 | else: 89 | best = float('-inf') 90 | result = None 91 | for s in solutions: 92 | p = 0 93 | for i in range(m): 94 | p += pleasure[i] * s[i] 95 | if p > best: 96 | best = p 97 | result = s 98 | temp = 0 99 | for e in result: 100 | temp += e 101 | if temp > 1000000000: 102 | print('Infinity') 103 | else: 104 | print('Bounded solution') 105 | for e in result: 106 | print("{0:.18f}".format(e), end=' ') 107 | 108 | 109 | if __name__ == '__main__': 110 | n_equations, n_variables = map(int, input().split()) 111 | A = [] 112 | for i in range(n_equations): 113 | A.append(list(map(int, input().split()))) 114 | B = list(map(int, input().split())) 115 | pleasure = list(map(int, input().split())) 116 | for i in range(n_variables): 117 | lst = [0] * n_variables 118 | lst[i] = -1 119 | A.append(lst) 120 | B.append(0) 121 | A.append([1] * n_variables) 122 | B.append(1000000001) 123 | # print('A', A, 'B', B) 124 | sub = FindSubsets(n_equations, n_variables) 125 | # print('subsets', sub) 126 | 127 | Solve(sub, A, B, pleasure, n_variables) 128 | exit(0) 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week2-Linear Programming/3-Two-Phase Simplex.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | def ChoosePivotColumn(m, n, s, A): 5 | minimum = -0.001 6 | enter = - 1 7 | # choose pivot column 8 | for j in range(m + n + s + 2): 9 | if A[-1][j] < minimum: 10 | minimum = A[-1][j] 11 | enter = j # pivot.column 12 | return enter 13 | 14 | 15 | # Gauss-Jordan elimination 16 | def Pivoting(depart, enter, m, n, s, A, b): 17 | pivot = A[depart][enter] 18 | if pivot != 1: 19 | for k in range(m + n + s + 2): 20 | A[depart][k] /= pivot 21 | b[depart] /= pivot 22 | for i in range(len(A)): 23 | if i != depart: 24 | a = A[i][enter] 25 | for j in range(m + n + s + 2): 26 | A[i][j] -= A[depart][j] * a 27 | b[i] -= b[depart] * a 28 | 29 | 30 | def PhaseOne(m, n, s, A, b, basis): 31 | enter = ChoosePivotColumn(m, n, s, A) # pivot column 32 | while enter != -1: 33 | # choose pivot 34 | depart = -1 35 | min_ratio = float('inf') 36 | for i in range(n): 37 | if A[i][enter] > 0: 38 | ratio = b[i]/A[i][enter] 39 | if ratio < min_ratio: 40 | min_ratio = ratio 41 | depart = i # pivot.row 42 | basis[depart] = enter 43 | Pivoting(depart, enter, m, n, s, A, b) 44 | enter = ChoosePivotColumn(m, n, s, A) 45 | return b[-1] 46 | 47 | 48 | def Transition(m, n, s, A, basis, b, arti): 49 | enter = -1 50 | for i in range(n): 51 | if basis[i] in arti: 52 | for j in range(m + n): 53 | if A[i][j] != 0: 54 | enter = j 55 | basis[i] = j 56 | break 57 | Pivoting(i, enter, m, n, s, A, b) 58 | 59 | 60 | def PhaseTwo(m, n, s, A, b, basis): 61 | ans = 0 62 | A.pop() 63 | b.pop() 64 | enter = ChoosePivotColumn(m, n, -2, A) # pivot column 65 | while enter != -1: 66 | # choose pivot 67 | depart = -1 68 | min_ratio = float('inf') 69 | for i in range(n): 70 | if A[i][enter] > 0: 71 | ratio = b[i]/A[i][enter] 72 | if ratio < min_ratio: 73 | min_ratio = ratio 74 | depart = i # pivot.row 75 | if depart == -1: 76 | ans = 1 77 | break 78 | else: 79 | basis[depart] = enter 80 | Pivoting(depart, enter, m, n, s, A, b) 81 | enter = ChoosePivotColumn(m, n, -2, A) 82 | return ans 83 | 84 | 85 | def TwoPhaseSimplex(m, n, s, A, b, basis, arti_var): 86 | w = PhaseOne(m, n, s, A, b, basis) 87 | if w < -0.001: 88 | print('No solution') 89 | else: 90 | Transition(m, n, s, A, basis, b, arti_var) 91 | ans = PhaseTwo(m, n, s, A, b, basis) 92 | if ans == 1: 93 | print('Infinity') 94 | else: 95 | solution = [0] * (m + n) 96 | for i in range(n): 97 | solution[basis[i]] = b[i] 98 | print('Bounded solution') 99 | for k in range(m): 100 | print("{0:.18f}".format(solution[k]), end=' ') 101 | 102 | 103 | if __name__ == '__main__': 104 | n, m = map(int, input().split()) 105 | A = [] 106 | for _ in range(n): 107 | A += [list(map(int, input().split()))] 108 | b = list(map(int, input().split())) 109 | c = list(map(int, input().split())) 110 | b.append(0) 111 | arti = [] 112 | for i in range(n + 1): 113 | if b[i] < 0: 114 | arti.append(i) 115 | s = len(arti) 116 | basis = [] 117 | arti_var = [] 118 | count = 0 119 | for i in range(n): 120 | lst = [0] * (n + s + 2) 121 | lst[i] = 1 122 | if b[i] >= 0: 123 | A[i] += lst 124 | basis.append(m + i) 125 | else: 126 | b[i] = -b[i] 127 | temp = [-e for e in A[i]] 128 | temp += [-e for e in lst] 129 | temp[n + m + count] = 1 130 | basis.append(n + m + count) 131 | arti_var.append(n + m + count) 132 | count += 1 133 | A[i] = temp 134 | temp = [-e for e in c] + [0] * (n + s + 2) 135 | temp[-2] = 1 136 | A += [temp] 137 | # w 138 | temp = [] 139 | for j in range(m + n): 140 | a = 0 141 | for e in arti: 142 | a += A[e][j] 143 | temp.append(-a) 144 | temp += [0] * (s + 2) 145 | temp[-1] = 1 146 | A += [temp] 147 | a = 0 148 | for e in arti: 149 | a += b[e] 150 | b.append(-a) 151 | TwoPhaseSimplex(m, n, s, A, b, basis, arti_var) 152 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week3-NP-completeness/1-3-color to SAT.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | class Node: 5 | def __init__(self, i): 6 | self.r = 3 * i - 2 7 | self.g = 3 * i - 1 8 | self.b = 3 * i 9 | 10 | 11 | n_vertices, n_edges = map(int, input().split()) 12 | edges = [list(map(int, input().split())) for i in range(n_edges)] 13 | 14 | V = [] 15 | for i in range(1, n_vertices + 1): 16 | node = Node(i) 17 | V.append([node.r, node.g, node.b]) 18 | V.append([-node.r, -node.g]) 19 | V.append([-node.r, -node.b]) 20 | V.append([-node.g, -node.b]) 21 | 22 | E = [] 23 | for a, b in edges: 24 | u = Node(a) 25 | v = Node(b) 26 | E.append([-u.r, -v.r]) 27 | E.append([-u.g, -v.g]) 28 | E.append([-u.b, -v.b]) 29 | 30 | n_clauses = len(V) + len(E) 31 | n_variables = n_vertices * 3 32 | print(n_clauses, n_variables) 33 | 34 | for clause in V: 35 | for i in clause: 36 | print(i, end=' ') 37 | print(0) 38 | 39 | for clause in E: 40 | for i in clause: 41 | print(i, end=' ') 42 | print(0) 43 | 44 | 45 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week3-NP-completeness/2-Hamitonian path to SAT.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | import itertools 5 | 6 | 7 | n, m = map(int, input().split()) 8 | edges = [list(map(int, input().split())) for i in range(m)] 9 | 10 | V = [] 11 | for i in range(1, n + 1): 12 | # each node must appear in the path 13 | Xij = list(j for j in range((i-1) * n + 1, i * n + 1)) 14 | V.append(Xij) 15 | # each node can only appear once in the path 16 | lst = list(-x for x in Xij) 17 | subsets = list(map(list, itertools.combinations(lst, 2))) 18 | V += subsets 19 | 20 | E = [] 21 | for i in range(1, n + 1): 22 | # every position in the path must be occupied 23 | pos = list(k for k in range(i, n * n + i, n)) 24 | E.append(pos) 25 | # every position in the path can only have one node 26 | lst = list(-x for x in pos) 27 | subsets = list(map(list, itertools.combinations(lst, 2))) 28 | E += subsets 29 | 30 | lst = list(i for i in range(1, n + 1)) 31 | subsets = list(map(list, itertools.combinations(lst, 2))) 32 | C = [] 33 | # Nonadjacent nodes can't be adjacent in the path 34 | for a, b in subsets: 35 | if [a,b] not in edges and [b,a] not in edges: 36 | for i in range(1, n): 37 | C.append([-((a - 1) * n + i), -((b - 1) * n + i + 1)]) 38 | C.append([-((b - 1) * n + i), -((a - 1) * n + i + 1)]) 39 | 40 | n_clauses = len(V) + len(E) + len(C) 41 | n_variables = n * n 42 | print(n_clauses, n_variables) 43 | 44 | for clause in V: 45 | for i in clause: 46 | print(i, end=' ') 47 | print(0) 48 | 49 | for clause in E: 50 | for i in clause: 51 | print(i, end=' ') 52 | print(0) 53 | 54 | for clause in C: 55 | for i in clause: 56 | print(i, end=' ') 57 | print(0) 58 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week3-NP-completeness/3-binary LP to SAT.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | n, m = list(map(int, input().split())) 5 | A = [] 6 | for i in range(n): 7 | A += [list(map(int, input().split()))] 8 | b = list(map(int, input().split())) 9 | 10 | lst1 = [0, 1] 11 | lst2 = [[0, 0], [1, 0], [0, 1], [1, 1]] 12 | lst3 = [[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1], [0, 1, 1], [1, 1, 1]] 13 | 14 | clauses = [] 15 | for i in range(n): 16 | # find non-zero coefficients 17 | C = [] # non-zero coefficients 18 | for j in range(m): 19 | if A[i][j] != 0: 20 | C.append(j) 21 | if len(C) == 0: 22 | if b[i] < 0: 23 | clauses = [[1], [-1]] 24 | break 25 | else: 26 | continue 27 | # add clauses which make inequalities unsatisfiable 28 | elif len(C) == 1: 29 | for u in lst1: 30 | if A[i][C[0]] * u > b[i]: 31 | if u == 0: 32 | clauses.append([C[0] + 1]) 33 | else: 34 | clauses.append([- C[0] - 1]) 35 | elif len(C) == 2: 36 | for u, v in lst2: 37 | if A[i][C[0]] * u + A[i][C[1]] * v > b[i]: 38 | temp = [] 39 | for k in range(2): 40 | if [u, v][k] == 0: 41 | temp += [C[k] + 1] 42 | else: 43 | temp += [- C[k] - 1] 44 | clauses.append(temp) 45 | elif len(C) == 3: 46 | for u, v, w in lst3: 47 | if A[i][C[0]] * u + A[i][C[1]] * v + A[i][C[2]] * w > b[i]: 48 | temp = [] 49 | for k in range(3): 50 | if [u, v, w][k] == 0: 51 | temp += [C[k] + 1] 52 | else: 53 | temp += [- C[k] - 1] 54 | clauses.append(temp) 55 | 56 | if len(clauses) == 0: 57 | clauses = [[1, -1]] 58 | n_variables = 1 59 | elif clauses == [[1], [-1]]: 60 | n_variables = 1 61 | else: 62 | n_variables = m 63 | 64 | print(len(clauses), n_variables) 65 | for clause in clauses: 66 | for i in clause: 67 | print(i, end=' ') 68 | print(0) 69 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week4-Coping with NP-completeness/1-integrated circuit.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | import sys 5 | import threading 6 | 7 | sys.setrecursionlimit(10**7) # max depth of recursion 8 | threading.stack_size(2**25) # new thread will get stack of such size 9 | 10 | 11 | def PostOrder(i, graph, visited, post): 12 | global clock 13 | visited[i] = True 14 | for v in graph[i]: 15 | if not visited[v]: 16 | PostOrder(v, graph, visited, post) 17 | post[i] = clock 18 | clock += 1 19 | 20 | 21 | def DFS(n, graph): 22 | global clock 23 | visited = [False] * (2 * n + 1) 24 | post = [0] * (2 * n + 1) 25 | for v in range(1, 2 * n + 1): 26 | if not visited[v]: 27 | PostOrder(v, graph, visited, post) 28 | post = list(enumerate(post[1:], start=1)) 29 | post.sort(key=lambda x:x[1], reverse=True) 30 | post_vertex = [] 31 | for v, order in post: 32 | post_vertex.append(v) 33 | return post_vertex 34 | 35 | 36 | def Explore(i, graph, visited, SCC, SCC_number, u): 37 | visited[i] = True 38 | SCC.append(i) 39 | SCC_number[i] = u 40 | for v in graph[i]: 41 | if not visited[v]: 42 | Explore(v, graph, visited, SCC, SCC_number, u) 43 | 44 | def FindSCCs(n, rev_graph, graph): 45 | global clock 46 | post_vertex = DFS(n, rev_graph) 47 | visited = [False] * (2 * n + 1) 48 | SCCs = [] 49 | SCC_number = [0] * (2 * n + 1) 50 | u = 1 51 | for i in post_vertex: 52 | if not visited[i]: 53 | SCC = [] 54 | Explore(i, graph, visited, SCC, SCC_number, u) 55 | SCCs.append(SCC) 56 | u += 1 57 | return SCCs, SCC_number 58 | 59 | 60 | def TwoSAT(n, rev_graph, graph): 61 | SCCs, SCC_number = FindSCCs(n, rev_graph, graph) 62 | # print(SCCs, SCC_number) 63 | for i in range(1, n + 1): 64 | if SCC_number[i] == SCC_number[i + n]: 65 | return False 66 | solution = [[] for _ in range(2 * n + 1)] 67 | assigned = [False] * (2 * n + 1) 68 | for SCC in SCCs: 69 | for v in SCC: 70 | if not assigned[v]: 71 | assigned[v] = True 72 | solution[v] = 1 73 | if v > n: 74 | solution[v - n] = 0 75 | assigned[v - n] = True 76 | else: 77 | solution[v + n] = 0 78 | assigned[v + n] = True 79 | return solution 80 | 81 | 82 | clock = 1 83 | def main(): 84 | n, m = map(int, input().split()) 85 | edges = [[] for _ in range(2 * n + 1)] 86 | rev_edges = [[] for _ in range(2 * n + 1)] 87 | for _ in range(m): 88 | a, b = map(int, input().split()) 89 | if a > 0 and b > 0: 90 | edges[a + n].append(b) 91 | edges[b + n].append(a) 92 | rev_edges[b].append(a + n) 93 | rev_edges[a].append(b + n) 94 | elif a < 0 and b < 0: 95 | edges[-a].append(-b + n) 96 | edges[-b].append(-a + n) 97 | rev_edges[-b + n].append(-a) 98 | rev_edges[-a + n].append(-b) 99 | elif a < 0 and b > 0 : 100 | edges[-a].append(b) 101 | edges[b + n].append(-a + n) 102 | rev_edges[b].append(-a) 103 | rev_edges[-a + n].append(b + n) 104 | elif a > 0 and b < 0: 105 | edges[a + n].append(-b + n) 106 | edges[-b].append(a) 107 | rev_edges[-b + n].append(a + n) 108 | rev_edges[a].append(-b) 109 | # print(edges) 110 | # print(rev_edges) 111 | result = TwoSAT(n, rev_edges, edges) 112 | if not result: 113 | print('UNSATISFIABLE') 114 | else: 115 | print('SATISFIABLE') 116 | for i in range(1, n + 1): 117 | if result[i] > 0: 118 | print(i, end=' ') 119 | else: 120 | print(-i, end=' ') 121 | 122 | 123 | threading.Thread(target=main).start() 124 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week4-Coping with NP-completeness/2-bidirectional edge.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | import sys 5 | import threading 6 | 7 | # This code is used to avoid stack overflow issues 8 | sys.setrecursionlimit(10**6) # max depth of recursion 9 | threading.stack_size(2**26) # new thread will get stack of such size 10 | 11 | 12 | class Vertex: 13 | def __init__(self, weight): 14 | self.weight = weight 15 | self.children = [] 16 | 17 | 18 | def ReadTree(): 19 | size = int(input()) 20 | tree = [Vertex(w) for w in map(int, input().split())] 21 | for i in range(1, size): 22 | a, b = list(map(int, input().split())) 23 | tree[a - 1].children.append(b - 1) 24 | tree[b - 1].children.append(a - 1) 25 | return tree 26 | 27 | 28 | def DFS(tree, v, parent, D): 29 | if D[v] == - 1: 30 | if len(tree[v].children) == 1 and v != 0: 31 | D[v] = tree[v].weight 32 | else: 33 | m1 = tree[v].weight 34 | for u in tree[v].children: 35 | if u != parent: 36 | for w in tree[u].children: 37 | if w != v: 38 | m1 += DFS(tree, w, u, D) 39 | m0 = 0 40 | for u in tree[v].children: 41 | if u != parent: 42 | m0 += DFS(tree, u, v, D) 43 | D[v] = max(m0, m1) 44 | return D[v] 45 | 46 | 47 | def MaxWeightIndependentTreeSubset(tree): 48 | size = len(tree) 49 | if size == 0: 50 | return 0 51 | D = [-1] * size 52 | return DFS(tree, 0, -1, D) 53 | 54 | 55 | def main(): 56 | tree = ReadTree() 57 | weight = MaxWeightIndependentTreeSubset(tree) 58 | print(weight) 59 | 60 | 61 | # This is to avoid stack overflow issues 62 | threading.Thread(target=main).start() 63 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week4-Coping with NP-completeness/3-traveling salesman.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | from itertools import permutations 5 | from itertools import combinations 6 | from collections import deque 7 | 8 | INF = 10 ** 9 9 | 10 | def read_data(): 11 | n, m = map(int, input().split()) 12 | graph = [[INF] * n for _ in range(n)] # adjacent matrix 13 | for _ in range(m): 14 | u, v, weight = map(int, input().split()) 15 | u -= 1 16 | v -= 1 17 | graph[u][v] = graph[v][u] = weight 18 | return graph 19 | 20 | 21 | def print_answer(path_weight, path): 22 | print(path_weight) 23 | if path_weight == -1: 24 | return 25 | print(' '.join(map(str, path))) 26 | 27 | 28 | def optimal_path(graph): 29 | n = len(graph) 30 | C = [[INF for _ in range(n)] for __ in range(1 << n)] # 纵坐标是1~2^n(因为共2^n个子集S), 横坐标是1~n 31 | backtrack = [[(-1, -1) for _ in range(n)] for __ in range(1 << n)] 32 | C[1][0] = 0 33 | for size in range(1, n): 34 | for S in combinations(range(1, n), size): 35 | S = (0,) + S 36 | k = sum([1 << i for i in S]) 37 | for i in S: 38 | if i != 0: 39 | for j in S: 40 | if j != i: 41 | m = k ^ (1 << i) 42 | curr = C[m][j] + graph[j][i] 43 | if curr < C[k][i]: 44 | C[k][i] = curr 45 | backtrack[k][i] = (m, j) 46 | best_ans, index2 = min([(C[-1][i] + graph[i][0], i) for i in range(n)]) 47 | 48 | if best_ans >= INF: 49 | return -1, [] 50 | 51 | path = deque() 52 | index1 = (1 << n) - 1 53 | while index1 != -1: 54 | path.appendleft(index2 + 1) 55 | index1, index2 = backtrack[index1][index2] 56 | return best_ans, path 57 | 58 | 59 | if __name__ == '__main__': 60 | print_answer(*optimal_path(read_data())) 61 | -------------------------------------------------------------------------------- /5-Advanced Algorithms and Complexity/Week4-Coping with NP-completeness/4-reschedule exam.py: -------------------------------------------------------------------------------- 1 | # python3 2 | 3 | 4 | import sys 5 | import threading 6 | 7 | sys.setrecursionlimit(10**7) # max depth of recursion 8 | threading.stack_size(2**25) # new thread will get stack of such size 9 | 10 | 11 | class Node: 12 | def __init__(self, i): 13 | self.r = 3 * i - 2 14 | self.g = 3 * i - 1 15 | self.b = 3 * i 16 | 17 | 18 | def ReduceToTwoSAT(colors, edges): 19 | n = len(colors) 20 | C = [] 21 | for i in range(1, n + 1): 22 | node = Node(i) 23 | if colors[i - 1] == 'R': 24 | # C.append((-node.r)) 25 | C.append((node.g, node.b)) 26 | C.append((-node.g, -node.b)) 27 | elif colors[i - 1] == 'G': 28 | # C.append((-node.g)) 29 | C.append((node.r, node.b)) 30 | C.append((-node.r, -node.b)) 31 | elif colors[i - 1] == 'B': 32 | # C.append((-node.b)) 33 | C.append((node.r, node.g)) 34 | C.append((-node.r, -node.g)) 35 | for a, b in edges: 36 | u, v = Node(a), Node(b) 37 | C.append((-u.r, -v.r)) 38 | C.append((-u.g, -v.g)) 39 | C.append((-u.b, -v.b)) 40 | return C 41 | 42 | 43 | def TwoSATtoGraph(n, C): 44 | graph = [[] for _ in range(2 * n + 1)] # 共3n个变量 45 | rev_graph = [[] for _ in range(2 * n + 1)] 46 | for a,b in C: 47 | if a > 0 and b > 0: 48 | graph[a + n].append(b) 49 | graph[b + n].append(a) 50 | rev_graph[b].append(a + n) 51 | rev_graph[a].append(b + n) 52 | elif a < 0 and b < 0: 53 | graph[-a].append(-b + n) 54 | graph[-b].append(-a + n) 55 | rev_graph[-b + n].append(-a) 56 | rev_graph[-a + n].append(-b) 57 | elif a < 0 and b > 0: 58 | graph[-a].append(b) 59 | graph[b + n].append(-a + n) 60 | rev_graph[b].append(-a) 61 | rev_graph[-a + n].append(b + n) 62 | elif a > 0 and b < 0: 63 | graph[a + n].append(-b + n) 64 | graph[-b].append(a) 65 | rev_graph[-b + n].append(a + n) 66 | rev_graph[a].append(-b) 67 | return graph, rev_graph 68 | 69 | 70 | def PostOrder(i, graph, visited, post): 71 | global clock 72 | visited[i] = True 73 | for v in graph[i]: 74 | if not visited[v]: 75 | PostOrder(v, graph, visited, post) 76 | post[i] = clock 77 | clock += 1 78 | 79 | 80 | def DFS(n, graph): 81 | global clock 82 | visited = [False] * (2 * n + 1) 83 | post = [0] * (2 * n + 1) 84 | for v in range(1, 2 * n + 1): 85 | if not visited[v]: 86 | PostOrder(v, graph, visited, post) 87 | post = list(enumerate(post[1:], start=1)) 88 | post.sort(key=lambda x: x[1], reverse=True) 89 | post_vertex = [] 90 | for v, order in post: 91 | post_vertex.append(v) 92 | return post_vertex 93 | 94 | 95 | def Explore(i, graph, visited, SCC, SCC_number, u): 96 | visited[i] = True 97 | SCC.append(i) 98 | SCC_number[i] = u 99 | for v in graph[i]: 100 | if not visited[v]: 101 | Explore(v, graph, visited, SCC, SCC_number, u) 102 | 103 | 104 | def FindSCCs(n, rev_graph, graph): 105 | global clock 106 | post_vertex = DFS(n, rev_graph) 107 | visited = [False] * (2 * n + 1) 108 | SCCs = [] 109 | SCC_number = [0] * (2 * n + 1) 110 | u = 1 111 | for i in post_vertex: 112 | if not visited[i]: 113 | SCC = [] 114 | Explore(i, graph, visited, SCC, SCC_number, u) 115 | SCCs.append(SCC) 116 | u += 1 117 | return SCCs, SCC_number 118 | 119 | 120 | def SolveTwoSAT(n, rev_graph, graph): 121 | SCCs, SCC_number = FindSCCs(n, rev_graph, graph) 122 | for i in range(1, n + 1): 123 | if SCC_number[i] == SCC_number[i + n]: 124 | return False 125 | solution = [[] for _ in range(2 * n + 1)] 126 | assigned = [False] * (2 * n + 1) 127 | for SCC in SCCs: 128 | for v in SCC: 129 | if not assigned[v]: 130 | assigned[v] = True 131 | solution[v] = 1 132 | if v > n: 133 | solution[v - n] = 0 134 | assigned[v - n] = True 135 | else: 136 | solution[v + n] = 0 137 | assigned[v + n] = True 138 | return solution 139 | 140 | 141 | clock = 1 142 | def main(): 143 | n, m = map(int, input().split()) 144 | colors = input() 145 | edges = [] 146 | for _ in range(m): 147 | edges.append(tuple(map(int, input().split()))) 148 | C = ReduceToTwoSAT(colors, edges) 149 | graph, rev_graph = TwoSATtoGraph(3 * n, C) 150 | result = SolveTwoSAT(3 * n, rev_graph, graph) 151 | if not result: 152 | print('Impossible') 153 | else: 154 | for i in range(1, 3 * n + 1): 155 | if result[i] == 1: 156 | if i % 3 == 1: 157 | print('R', end='') 158 | elif i % 3 == 2: 159 | print('G', end='') 160 | elif i % 3 == 0: 161 | print('B', end='') 162 | 163 | 164 | threading.Thread(target=main).start() 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coursera-Data_Structures_and_Algorithms 2 | My solutions to assignments of [Data structures and algorithms](https://www.coursera.org/specializations/data-structures-algorithms) (by UCSD and HSE) on Coursera. 3 | All problems from course 1 to course 5 have been solved. 4 | 5 | Course 1: Algorithmic Toolbox [[Certificate]](https://www.coursera.org/account/accomplishments/certificate/P7UWAE5Z4NAJ?utm_medium=certificate&utm_source=link&utm_campaign=copybutton_certificate) 6 | - 7 | 8 | **Algorithmic Warm-up** 9 | 1. [Fibonacci Number](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/1-Fibonacci%20Number.py) 10 | 2. [Last Digit of a Large Fibonacci Number](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/2-last%20digit%20of%20a%20large%20fibonacci%20number.py) 11 | 3. [Greatest Common Divisor](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/3-greatest%20common%20divisor.py) 12 | 4. [Least Common Multiple](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/4-least%20common%20multiple.py) 13 | 5. [Fibonacci Number Again](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/5-Fibonacci%20number%20again.py) 14 | 6. [Last Digit of the Sum of Fibonacci Numbers](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/6-last%20digit%20of%20the%20sum%20of%20fibonacci%20numbers.py) 15 | 7. [Last Digit of the Sum of Fibonacci Numbers Again](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/7-last%20digit%20of%20the%20sum%20of%20fibonacci%20numbers%20again.py) 16 | 8. [Last Digit of the Sum of Squares of Fibonacci Numbers](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week1-Algorithmic%20Warm-up/8-last%20digit%20of%20the%20sum%20of%20squares%20of%20Fibonacci%20numbers.py) 17 | 18 | **Greedy Algorithms** 19 | 1. [Money Change](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/1-money%20change.py) 20 | 2. [Maximum Value of the Loot](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/2-max%20value%20of%20the%20loot.py) 21 | 3. [Car Fueling](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/3-car%20fueling.py) 22 | 4. [Maximum Advertisement Revenue](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/4-max%20advertisement%20revenue.py) 23 | 5. [Collecting Signatures](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/5-collecting_signatures.py) 24 | 6. [Maximum Number of Prizes](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/6-max%20number%20of%20prizes.py) 25 | 7. [Maximum Salary](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week3-Greedy%20Algorithms/7-max%20salary.py) 26 | 27 | **Divide and Conquer** 28 | 1. [Binary Search](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/1-binary%20search.py) 29 | 2. [Majority Element](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/2-majority%20element.py) 30 | 3. [Improving Quick Sort](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/3-3-way%20quick%20sort.py) 31 | 4. [Number of Inversions](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/4-number%20of%20inversions.py) 32 | 5. Organizing a Lottery [[naive]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/5-organizing%20a%20lottery_naive.py) [[faster]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/5-organizing_a_lottery_faster.py) 33 | 6. [Closest Points](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week4-Divide%20and%20Conquer/6-closest%20points.py) 34 | 35 | **Dynamic Programming 1** 36 | 1. [Money Change Again](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week5-Dynamic%20Programming%201/1-money%20change%20agian.py) 37 | 2. [Primitive Calculator](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week5-Dynamic%20Programming%201/2-primitive%20operations.py) 38 | 3. [Edit Distance](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week5-Dynamic%20Programming%201/3-edit%20distance.py) 39 | 4. [Longest Common Subsequence of Two Sequences](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week5-Dynamic%20Programming%201/4-longest%20common%20subsequence%20of%202%20sequences.py) 40 | 5. [Longest Common Subsequence of Three Sequences](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week5-Dynamic%20Programming%201/5-longest%20common%20subsequence%20of%203%20sequences.py) 41 | 42 | **Dynamic Programming 2** 43 | 1. [Maximum Amount of Gold](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week6-Dynamic%20Programming%202/1-maximum%20amount%20of%20gold.py) 44 | 2. [Partitioning Souvenirs](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week6-Dynamic%20Programming%202/2-partitioning%20souvenirs.py) 45 | 3. [Maximum Value of an Arithmetic Expression](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/1-Algorithmic%20Toolbox/Week6-Dynamic%20Programming%202/3-maximum%20value%20of%20an%20arithmetic%20expression.py) 46 | 47 | Course 2: Data Structures [[Certificate]](https://www.coursera.org/account/accomplishments/verify/SNXB3ZMZXNTD) 48 | - 49 | 50 | **Basic Data Structures** 51 | 1. [Check brackets in the code](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week1-Basic%20Data%20Structures/1-check%20brackets%20in%20the%20code.py) 52 | 2. [Compute tree height](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week1-Basic%20Data%20Structures/2-compute%20tree%20height.py) 53 | 3. [Network packet processing simulation](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week1-Basic%20Data%20Structures/3-packet%20processing.py) 54 | 4. [Extending stack interface](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week1-Basic%20Data%20Structures/4-extending%20stack%20interface.py) 55 | 5. [Maximum in Sliding Window](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week1-Basic%20Data%20Structures/5-maximum%20in%20sliding%20window.py) 56 | 57 | **Priority Queues and Disjoint Sets** 58 | 1. [Convert array into heap](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week3-Priority%20Queues%20and%20Disjoint%20Sets/1-convert%20array%20into%20heap.py) 59 | 2. [Parallel processing](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week3-Priority%20Queues%20and%20Disjoint%20Sets/2-parallel%20processing.py) 60 | 3. [Merging tables](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week3-Priority%20Queues%20and%20Disjoint%20Sets/3-merging%20tables.py) 61 | 62 | **Hash Tables and Hash Functions** 63 | 1. [Phone book](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/1-phone%20book.py) 64 | 2. [Hashing with chains](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/2-hashing%20with%20chains.py) 65 | 3. [Find pattern in text](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/3-find%20pattern%20in%20text.py) 66 | 4. [Substring equality](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/4-substring%20equality.py) 67 | 5. [Longest common substring](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/5-longest%20common%20substring.py) 68 | 6. [Pattern matching with mismatches](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/6-pattern%20matching%20with%20mismatches.py) 69 | 70 | **Binary Search Trees** 71 | 1. [Binary tree traversals](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week4-Hash%20Tables%20and%20Hash%20Functions/6-pattern%20matching%20with%20mismatches.py) 72 | 2. Is it a binary search tree? [[in-order traversal]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week6-Binary%20Search%20Trees/2-is%20binary%20search%20tree.py) [[min/max constraint]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week6-Binary%20Search%20Trees/2-min%20max%20method.py) 73 | 3. [Is it a binary search tree? Hard version](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week6-Binary%20Search%20Trees/3-is%20bst%20hard.py) 74 | 4. [Set with range sums](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week6-Binary%20Search%20Trees/4-splay%20tree.py) 75 | 5. Rope [[naive method]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week6-Binary%20Search%20Trees/5-naive.py) [[rope structure]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/2-Data%20Structures/Week6-Binary%20Search%20Trees/5-rope%20structure.py) 76 | 77 | Course 3: Algorithms on Graphs [[Certificate]](https://www.coursera.org/account/accomplishments/verify/P7UWAE5Z4NAJ) 78 | - 79 | 80 | **Undirected Graphs** 81 | 1. [Finding an Exit from a Maze](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week1-Undirected%20Graphs/1-reachability.py) 82 | 2. [Adding Exits to a Maze](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week1-Undirected%20Graphs/2-connected%20components.py) 83 | 84 | **Directed Graphs** 85 | 1. [Checking Consistency of CS Curriculum](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week2-Directed%20Graphs/1-directed%20acyclic%20graphs.py) 86 | 2. [Determining an Order of Courses](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week2-Directed%20Graphs/2-topological%20sort.py) 87 | 3. [Advanced Problem: Checking Whether Any Intersection in a City is Reachable from Any Other](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week2-Directed%20Graphs/3-strongly%20connected%20components.py) 88 | 89 | **Most Direct Route** 90 | 1. [Computing the Minimum Number of Flight Segments](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week3-Most%20Direct%20Route/1-breath%20first%20search.py) 91 | 2. [Checking whether a Graph is Bipartite](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week3-Most%20Direct%20Route/2-bipartite.py) 92 | 93 | **Fastest Route** 94 | 1. [Computing the Minimum Cost of a Flight](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week4-Fastest%20Route/1-Dijkstra_min-heap.py) 95 | 2. [Detecting Anomalies in Currency Exchange Rates](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week4-Fastest%20Route/2-negative%20cycle.py) 96 | 3. [Advanced Problem: Exchanging Money Optimally](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week4-Fastest%20Route/3-infinite%20arbitrage.py) 97 | 98 | **Minimum Spanning Trees** 99 | 1. [Building Roads to Connect Cities](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week5-Minimum%20Spanning%20Trees/1-connecting%20points.py) 100 | 2. [Clustering](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/3-Algorithms%20on%20Graphs/Week5-Minimum%20Spanning%20Trees/2-clustering.py) 101 | 102 | Course 4: Algorithms on Strings [[Certificate]](https://www.coursera.org/account/accomplishments/verify/PW9Y4GH4SPTX) 103 | - 104 | 105 | **Suffix Trees** 106 | 1. [Construct a Trie from a Collection of Patterns](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week1-Suffix%20Trees/1-constrcut%20a%20Trie%20from%20patterns.py) 107 | 2. [Implement TrieMatching](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week1-Suffix%20Trees/2-TrieMatching.py) 108 | 3. [Extend TrieMatching](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week1-Suffix%20Trees/3-Extend%20TrieMatching.py) 109 | 4. [Construct the Suffix Tree of a String](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week1-Suffix%20Trees/4-construct%20the%20suffix%20tree%20of%20a%20string.py) 110 | 5. [Advanced Problem: Find the Shortest Non-Shared Substring of Two Strings](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week1-Suffix%20Trees/5-shortest%20non-shared%20substring.py) 111 | 112 | **Burrows–Wheeler Transform and Suffix Arrays** 113 | 1. [Construct the Burrows–Wheeler Transform of a String](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week2-Burrows%E2%80%93Wheeler%20Transform%20and%20Suffix%20Arrays/1-construct%20BWT.py) 114 | 2. [Reconstruct a String from its Burrows–Wheeler Transform](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week2-Burrows%E2%80%93Wheeler%20Transform%20and%20Suffix%20Arrays/2-invert%20BWT.py) 115 | 3. [Implement BetterBWMatching](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week2-Burrows%E2%80%93Wheeler%20Transform%20and%20Suffix%20Arrays/3-BWmatching.py) 116 | 4. [Construct the Suffix Array of a String](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week2-Burrows%E2%80%93Wheeler%20Transform%20and%20Suffix%20Arrays/4-suffix%20array.py) 117 | 118 | **Algorithmic Challenges** 119 | 1. [Find All Occurrences of a Pattern in a String](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week4-Algorithmic%20Challenges/1-Knuth%20Morris%20Pratt.py) 120 | 2. [Construct the Suffix Array of a Long String](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week4-Algorithmic%20Challenges/2-suffix%20array%20for%20a%20long%20string.py) 121 | 3. [Pattern Matching with the Suffix Array](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week4-Algorithmic%20Challenges/3-suffix%20array%20matching.py) 122 | 4. [Advanced Problem: Construct the Suffix Tree from the Suffix Array](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/4-Algorithms%20on%20Strings/Week4-Algorithmic%20Challenges/4-suffix%20tree%20from%20array.py) 123 | 124 | Course 5: Advanced Algorithms and Complexity [[Certificate]](https://www.coursera.org/account/accomplishments/verify/A78PQPN5KJAB) 125 | - 126 | 127 | **Flows in Networks** 128 | 1. [Evacuating People](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week1-Flows%20in%20Networks/1-max%20flow.py) 129 | 2. [Assigning Airline Crews to Flights](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week1-Flows%20in%20Networks/2-bipartite%20matching.py) 130 | 3. [Advanced Problem: Stock Charts](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week1-Flows%20in%20Networks/3-stock%20charts.py) 131 | 132 | **Linear Programming** 133 | 1. Infer Energy Values of Ingredients [[1]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week2-Linear%20Programming/1-Gaussian%20Elimination.py) [[2]](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week2-Linear%20Programming/1b.py) 134 | 2. [Optimal Diet Problem](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week2-Linear%20Programming/2-LP%20brute%20force.py) 135 | 3. [Advanced Problem: Online Advertisement Allocation (Two-Phase Simplex)](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week2-Linear%20Programming/3-Two-Phase%20Simplex.py) 136 | 137 | **NP-completeness** 138 | 1. [Assign Frequencies to the Cells of a GSM Network](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week3-NP-completeness/1-3-color%20to%20SAT.py) 139 | 2. [Cleaning the Apartment](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week3-NP-completeness/2-Hamitonian%20path%20to%20SAT.py) 140 | 3. [Advanced Problem: Advertisement Budget Allocation](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week3-NP-completeness/3-binary%20LP%20to%20SAT.py) 141 | 142 | **Coping with NP-completeness** 143 | 1. [Integrated Circuit Design](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week4-Coping%20with%20NP-completeness/1-integrated%20circuit.py) 144 | 2. [Plan a Fun Party](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week4-Coping%20with%20NP-completeness/2-bidirectional%20edge.py) 145 | 3. [School Bus (Traveling Salesman)](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week4-Coping%20with%20NP-completeness/3-traveling%20salesman.py) 146 | 4. [Advanced Problem: Reschedule the Exams](https://github.com/Sonia-96/Coursera-Data-Structures-and-Algorithms/blob/master/5-Advanced%20Algorithms%20and%20Complexity/Week4-Coping%20with%20NP-completeness/4-reschedule%20exam.py) 147 | --------------------------------------------------------------------------------