Explanation for Booth's Algorithm for Lexicographically minimal string rotation - algorithm

Can someone please explain or comment me this code for Booth's Algorithm for Lexicographically minimal string rotation from wikipedia (https://en.wikipedia.org/wiki/Lexicographically_minimal_string_rotation#Booth.27s_Algorithm)?
def least_rotation(S: str) -> int:
"""Booth's algorithm."""
S += S # Concatenate string to it self to avoid modular arithmetic
f = [-1] * len(S) # Failure function
k = 0 # Least rotation of string found so far
for j in range(1, len(S)):
sj = S[j]
i = f[j - k - 1]
while i != -1 and sj != S[k + i + 1]:
if sj < S[k + i + 1]:
k = j - i - 1
i = f[i]
if sj != S[k + i + 1]: # if sj != S[k+i+1], then i == -1
if sj < S[k]: # k+i+1 = k
k = j
f[j - k] = -1
else:
f[j - k] = i + 1
return k
I am lost with the indices and the k etc. What does it mean, why is there [j - k - 1] indexing etc.?
Thanks!

Related

Algorithm problem about Google kick start round B 2021

I'm solving the longest progression problem in Google kick start 2021 Round B using python.
Here is the link to the problem: https://codingcompetitions.withgoogle.com/kickstart/round/0000000000435a5b
I have written the following code but it seems that there's always the wrong answer in a test case, I have tried all situations as far as I concerned, can someone give me the help that where's the problem in my code, thanks!
def solution(A, N):
i, j = 0, 1
ranges = {}
res = 0
left = {}
right = {}
while j < N:
diff = A[j] - A[i]
while j < N and A[j]-A[i] == (j-i)*diff:
j += 1
ranges[(i, j-1)] = diff
left[i] = (i, j-1)
right[j-1] = (i, j-1)
if j <= N-1 or i > 0:
res = max(res, j-i+1)
else:
res = max(res, j-i)
i = j-1
# check if two ranges can be merged
for i in range(1, N-1):
if i == 1:
if i+1 in left:
l1, r1 = left[i+1]
if A[i+1]-A[i-1] == 2*ranges[left[i+1]]:
res = max(res, r1-l1+3)
elif i == N-2:
if i-1 in right:
l1, r1 = right[i-1]
if A[i + 1] - A[i - 1] == 2 * ranges[right[i - 1]]:
res = max(res, r1 - l1 + 3)
else:
if i+1 in left and i-1 in right and ranges[right[i-1]] == ranges[left[i+1]]:
l1, r1 = right[i - 1]
l2, r2 = left[i+1]
if A[i+1]-A[i-1] == 2*ranges[left[i+1]]:
res = max(r1-l1+r2-l2+3, res)
return res
if __name__ == "__main__":
T = int(input().strip())
for i in range(T):
N = int(input().strip())
A = list(map(int, input().strip().split(" ")))
res = solution(A, N)
print("Case #{}: {}".format(i+1, res))
The merging logic is incorrect. The code only tries to merge the entire ranges. In a simple failing case
1 2 3 6 5 4
it misses that replacing 6 with 4 would produce 1 2 3 4 5.

How can I solve this problem using dynamic programming?

Given a list of numbers, say [4 5 2 3], I need to maximize the sum obtained according to the following set of rules:
I need to select a number from the list and that number will be removed.
Eg. selecting 2 will have the list as [4 5 3].
If the number to be removed has two neighbours then I should get the result of this selection as the product of the currently selected number with one of its neighbours and this product summed up with the other neighbour. eg.: if I select 2 then I can have the result of this selction as 2 * 5 + 3.
If I select a number with only one neighbour then the result is the product of the selected number with its neighbour.
When their is only one number left then it is just added to the result till now.
Following these rules, I need to select the numbers in such an order that the result is maximized.
For the above list, if the order of selction is 4->2->3->5 then the sum obtained is 53 which is the maximum.
I am including a program which lets you pass as input the set of elements and gives all possible sums and also indicates the max sum.
Here's a link.
import itertools
l = [int(i) for i in input().split()]
p = itertools.permutations(l)
c, cs = 1, -1
mm = -1
for i in p:
var, s = l[:], 0
print(c, ':', i)
c += 1
for j in i:
print(' removing: ', j)
pos = var.index(j)
if pos == 0 or pos == len(var) - 1:
if pos == 0 and len(var) != 1:
s += var[pos] * var[pos + 1]
var.remove(j)
elif pos == 0 and len(var) == 1:
s += var[pos]
var.remove(j)
if pos == len(var) - 1 and pos != 0:
s += var[pos] * var[pos - 1]
var.remove(j)
else:
mx = max(var[pos - 1], var[pos + 1])
mn = min(var[pos - 1], var[pos + 1])
s += var[pos] * mx + mn
var.remove(j)
if s > mm:
mm = s
cs = c - 1
print(' modified list: ', var, '\n sum:', s)
print('MAX SUM was', mm, ' at', cs)
Consider 4 variants of the problem: those where every element gets consumed, and those where either the left, the right, or both the right and left elements are not consumed.
In each case, you can consider the last element to be removed, and this breaks the problem down into 1 or 2 subproblems.
This solves the problem in O(n^3) time. Here's a python program that solves the problem. The 4 variants of solve_ correspond to none, one or the other, or both of the endpoints being fixed. No doubt this program can be reduced (there's a lot of duplication).
def solve_00(seq, n, m, cache):
key = ('00', n, m)
if key in cache:
return cache[key]
assert m >= n
if n == m:
return seq[n]
best = -1e9
for i in range(n, m+1):
left = solve_01(seq, n, i, cache) if i > n else 0
right = solve_10(seq, i, m, cache) if i < m else 0
best = max(best, left + right + seq[i])
cache[key] = best
return best
def solve_01(seq, n, m, cache):
key = ('01', n, m)
if key in cache:
return cache[key]
assert m >= n + 1
if m == n + 1:
return seq[n] * seq[m]
best = -1e9
for i in range(n, m):
left = solve_01(seq, n, i, cache) if i > n else 0
right = solve_11(seq, i, m, cache) if i < m - 1 else 0
best = max(best, left + right + seq[i] * seq[m])
cache[key] = best
return best
def solve_10(seq, n, m, cache):
key = ('10', n, m)
if key in cache:
return cache[key]
assert m >= n + 1
if m == n + 1:
return seq[n] * seq[m]
best = -1e9
for i in range(n+1, m+1):
left = solve_11(seq, n, i, cache) if i > n + 1 else 0
right = solve_10(seq, i, m, cache) if i < m else 0
best = max(best, left + right + seq[n] * seq[i])
cache[key] = best
return best
def solve_11(seq, n, m, cache):
key = ('11', n, m)
if key in cache:
return cache[key]
assert m >= n + 2
if m == n + 2:
return max(seq[n] * seq[n+1] + seq[n+2], seq[n] + seq[n+1] * seq[n+2])
best = -1e9
for i in range(n + 1, m):
left = solve_11(seq, n, i, cache) if i > n + 1 else 0
right = solve_11(seq, i, m, cache) if i < m - 1 else 0
best = max(best, left + right + seq[i] * seq[n] + seq[m], left + right + seq[i] * seq[m] + seq[n])
cache[key] = best
return best
for c in [[1, 1, 1], [4, 2, 3, 5], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]:
print(c, solve_00(c, 0, len(c)-1, dict()))

Rabin-Karp constant-time substring hash lookup

Given a string S[0..N-1], I want to be able to get the hash of any of its substrings S[i..j] in O(1) time, with O(N) preprocessing. Here's what I have so far (in Python):
BASE = 31
word = "abcxxabc"
N = len(word)
powers = []
prefix_hashes = []
b = 1
h = 0
for i in range(N):
powers.append(b)
b *= BASE
h += (ord(word[i]) - ord('a') + 1)
h *= BASE
prefix_hashes.append(h)
def get_hash(i, j):
if i == 0:
return prefix_hashes[j]
return prefix_hashes[j] - prefix_hashes[i - 1] * powers[j - i + 1]
It works very well... in Python, where I don't need to worry about possible integer overflow. I want to be able to perform above operations (effectively rewrite the whole algorithm), but modulo some big prime, so that I fit in 32-bit integer arithmetic. Here's what I came up with:
MOD = 10**9 + 7
BASE = 31
word = "abcxxabc"
N = len(word)
powers = []
prefix_hashes = []
b = 1
h = 0
for i in range(N):
powers.append(b)
b = (b * BASE) % MOD
h += (ord(word[i]) - ord('a') + 1)
h = (h * BASE) % MOD
prefix_hashes.append(h)
def get_hash(i, j):
if i == 0:
return prefix_hashes[j]
return (prefix_hashes[j] - prefix_hashes[i - 1] * powers[j - i + 1]) % MOD
But the
prefix_hashes[i - 1] * powers_of_base[j - i + 1]
part can overflow quite easily for larger MOD values. How to go about it?

Confusion Regarding deepest pit within an Array

I got this question as prerequisite for an interview,
A non-empty zero-indexed array A consisting of N integers is given. A
pit in this array is any triplet of integers (P, Q, R) such that: 0 ≤
P < Q < R < N;
sequence [A[P], A[P+1], ..., A[Q]] is strictly decreasing, i.e. A[P] >
A[P+1] > ... > A[Q];
sequence A[Q], A[Q+1], ..., A[R] is strictly increasing, i.e. A[Q] <
A[Q+1] < ... < A[R].
The depth of a pit (P, Q, R) is the number min{A[P] − A[Q], A[R] −
A[Q]}. For example, consider array A consisting of 10 elements such
that:
A[0] = 0
A[1] = 1
A[2] = 3
A[3] = -2
A[4] = 0
A[5] = 1
A[6] = 0
A[7] = -3
A[8] = 2
A[9] = 3
Triplet (2, 3, 4) is one of pits in this array, because sequence
[A[2], A[3]] is strictly decreasing (3 > −2) and sequence [A[3], A[4]]
is strictly increasing (−2 < 0). Its depth is min{A[2] − A[3], A[4] −
A[3]} = 2.
Triplet (2, 3, 5) is another pit with depth 3.
Triplet (5, 7, 8) is yet another pit with depth 4. There is no pit in
this array deeper (i.e. having depth greater) than 4.
It says that Triplet (5, 7, 8) has the deepest pit depth of 4.
but isn't Triplet (2, 7, 9) has the deepest pit depth 6?
corresponding value of Triplet (2, 7, 9) is (3, -3, 3) and it also satisfies the conditions mentioned, i.e.
1) 0 ≤ P < Q < R < N
2) A[P] > A[P+1] > ... > A[Q] and A[Q] < A[Q+1] < ... < A[R]
so in this case min{A[P] − A[Q], A[R] − A[Q]} is 6.
What am i missing here?
P.S. if you think this post does not belong here in this forum then please point out where should i post it.
See the sequence from P to Q for 2 to 7.
It is 3 -2 0 1 0 -3.
sequence [A[P], A[P+1], ..., A[Q]] is strictly decreasing, i.e. A[P] > A[P+1] > ... > A[Q];
The rule says that this should be a decreasing sequence. But it isn't. 3>-2 but -2 is not greater than 0. Here the sequence breaks.
From 7 to 9. No problem as the sequence is increasing. -3<2<3.
answer of the deepest pit problem in swift :
func solution(_ array: [Int]) -> Int {
//guaranty we have at least three elements
if array.isEmpty {
print("isEmpty")
return -1
}
if array.count < 3 {
print("is less than 3")
return -1
}
//extremum point; max or min points
var extremumPoints = [Int]()
//adding first element
extremumPoints.append(array[0])
//calculate extremum points for 1 to one before last element
for i in 1..<(array.count - 1) {
let isRelativeExtremum = ((array[i] - array[i - 1]) * (array[i] - array[i + 1])) > 0
//we call a point semi-extremum if a point is equal to previous element or next element and not equal to previous element or next element
let isSemiExtremum = ((array[i] != array[i - 1]) && (array[i] == array[i + 1])) || ((array[i] != array[i + 1]) && (array[i] == array[i - 1]))
if isRelativeExtremum || isSemiExtremum {
extremumPoints.append(array[i])
}
}
//adding last element
extremumPoints.append(array[array.count - 1])
//we will hold depthes in this array
var depthes = [Int]()
for i in 1..<(extremumPoints.count - 1) {
let isBottomOfaPit = extremumPoints[i] < extremumPoints[i - 1] && extremumPoints[i] < extremumPoints[i + 1]
if isBottomOfaPit {
let d1 = extremumPoints[i - 1] - extremumPoints[i]
let d2 = extremumPoints[i + 1] - extremumPoints[i]
let d = min(d1, d2)
depthes.append(d)
}
}
//deepest pit
let deepestPit = depthes.max()
return deepestPit ?? -1
}
//****************************
let A = [0,1,3,-2,0,1,0,-3,2,3]
let deepestPit = solution(A)
print(deepestPit) // 4
def deepest(A):
def check(p, q, r, A):
if A[p] > A[q] and A[q] < A[r]:
return min(A[p] - A[q], A[r] - A[q])
else:
return -1
max_depth = 0
for i in range(1, len(A) - 2):
if A[i-1] > A[i] < A[i + 1]:
p = i
r = i
while 0 <= p and r <= len(A) - 1:
depth = check(p, i, r, A)
max_depth = max(max_depth, depth)
p -= 1
r += 1
return max_depth

Insertion Sort - troubles reading MIT Intro to Algos

The MIT Intro to Algorithms describes insertion sort as:
I wrote this in Python as:
def sort(A):
for j in range(1, len(A)):
key = A[j];
i = j - 1;
# i > 0 should be i >= 0
while i > 0 and A[i] > key:
A[i + 1] = A[i]
i = i - 1;
A[i + 1] = key
return A
However the line while i > 0 introduces a bug - the first two keys are in the wrong positions. Changing this to while i >= 0 fixes this problem.
Why is this not included in the MIT Intro book? Am I reading it wrong?
The algorithm in the book is assuming indexing from 1 to A.length, inclusive, which is why it starts at an index of 2. Python has array indexing from 0 to len(A) - 1. You corrected for that in your range, but you neglected to correct for it in the loop test. Doing so fixes the problem.
#pjs is exactly right. I'll add that a methodical way to convert an algorithm written for 1-based arrays to 0-based with no off-by-1 errors is to use the algorithm as-is except subtract 1 at each array reference and then use algebra to simplify. Here you'd end up with:
def sort(A):
for j in range(2, len(A) + 1): # +1 is because Python ranges exclude high limit
key = A[j - 1]
i = j - 1
while i > 0 and A[i - 1] > key:
A[i + 1 - 1] = A[i - 1]
i = i - 1
A[i + 1 - 1] = key
return A
Of course it's easy to simplify by removing + 1 - 1, since that's adding zero! The result works fine.
If you want to make the code prettier with the outer range starting at 1 rather than 2, make the substitution jj = j - 1, which (adding 1 to both sides) means j = jj + 1:
def sort(A):
for jj in range(1, len(A)):
key = A[jj + 1 - 1]
i = jj + 1 - 1
while i > 0 and A[i - 1] > key:
A[i] = A[i - 1]
i = i - 1
A[i] = key
return A
Again removing + 1 - 1s, we have
def sort(A):
for jj in range(1, len(A)):
key = A[jj]
i = jj
while i > 0 and A[i - 1] > key:
A[i] = A[i - 1]
i = i - 1
A[i] = key
return A
This looks great, and I'd stop here. But yet another variation is possible with the substitution ii = i - 1 or i = ii + 1.
def sort(A):
for jj in range(1, len(A)):
key = A[jj]
ii + 1 = jj
while ii + 1 > 0 and A[ii + 1 - 1] > key:
A[ii + 1] = A[ii + 1 - 1]
ii + 1 = ii + 1 - 1
A[ii + 1] = key
return A
Hmmm... Those assignments to ii look weird. But we can straighten everything out with algebra yet again.
def sort(A):
for jj in range(1, len(A)):
key = A[jj]
ii = jj - 1 # Subtracted 1 from both sides
while ii >= 0 and A[ii] > key: # x + 1 > 0 iff x >= 0
A[ii + 1] = A[ii]
ii = ii - 1 # Subtracted 1 from both sides and simplified
A[ii + 1] = key
return A
Lo and behold, we have the code you proposed. Works every time.

Resources