Algorithm problem about Google kick start round B 2021 - algorithm

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.

Related

Min Abs Sum task from codility

There is already a topic about this task, but I'd like to ask about my specific approach.
The task is:
Let A be a non-empty array consisting of N integers.
The abs sum of two for a pair of indices (P, Q) is the absolute value
|A[P] + A[Q]|, for 0 ≤ P ≤ Q < N.
For example, the following array A:
A[0] = 1 A1 = 4 A[2] = -3 has pairs of indices (0, 0), (0,
1), (0, 2), (1, 1), (1, 2), (2, 2). The abs sum of two for the pair
(0, 0) is A[0] + A[0] = |1 + 1| = 2. The abs sum of two for the pair
(0, 1) is A[0] + A1 = |1 + 4| = 5. The abs sum of two for the pair
(0, 2) is A[0] + A[2] = |1 + (−3)| = 2. The abs sum of two for the
pair (1, 1) is A1 + A1 = |4 + 4| = 8. The abs sum of two for the
pair (1, 2) is A1 + A[2] = |4 + (−3)| = 1. The abs sum of two for
the pair (2, 2) is A[2] + A[2] = |(−3) + (−3)| = 6. Write a function:
def solution(A)
that, given a non-empty array A consisting of N integers, returns the
minimal abs sum of two for any pair of indices in this array.
For example, given the following array A:
A[0] = 1 A1 = 4 A[2] = -3 the function should return 1, as
explained above.
Given array A:
A[0] = -8 A1 = 4 A[2] = 5 A[3] =-10 A[4] = 3 the
function should return |(−8) + 5| = 3.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000]; each element of array A
is an integer within the range [−1,000,000,000..1,000,000,000].
The official solution is O(N*M^2), but I think it could be solved in O(N).
My approach is to first get rid of duplicates and sort the array. Then we check both ends and sompare the abs sum moving the ends by one towards each other. We try to move the left end, the right one or both. If this doesn't improve the result, our sum is the lowest. My code is:
def solution(A):
A = list(set(A))
n = len(A)
A.sort()
beg = 0
end = n - 1
min_sum = abs(A[beg] + A[end])
while True:
min_left = abs(A[beg+1] + A[end]) if beg+1 < n else float('inf')
min_right = abs(A[beg] + A[end-1]) if end-1 >= 0 else float('inf')
min_both = abs(A[beg+1] + A[end-1]) if beg+1 < n and end-1 >= 0 else float('inf')
min_all = min([min_left, min_right, min_both])
if min_sum <= min_all:
return min_sum
if min_left == min_all:
beg += 1
min_sum = min_left
elif min_right == min_all:
end -= 1
min_sum = min_right
else:
beg += 1
end -= 1
min_sum = min_both
It passes almost all of the tests, but not all. Is there some bug in my code or the approach is wrong?
EDIT:
After the aka.nice answer I was able to fix the code. It scores 100% now.
def solution(A):
A = list(set(A))
n = len(A)
A.sort()
beg = 0
end = n - 1
min_sum = abs(A[beg] + A[end])
while beg <= end:
min_left = abs(A[beg+1] + A[end]) if beg+1 < n else float('inf')
min_right = abs(A[beg] + A[end-1]) if end-1 >= 0 else float('inf')
min_all = min(min_left, min_right)
if min_all < min_sum:
min_sum = min_all
if min_left <= min_all:
beg += 1
else:
end -= 1
return min_sum
Just take this example for array A
-11 -5 -2 5 6 8 12
and execute your algorithm step by step, you get a premature return:
beg=0
end=6
min_sum=1
min_left=7
min_right=3
min_both=3
min_all=3
return min_sum
though there is a better solution abs(5-5)=0.
Hint: you should check the sign of A[beg] and A[end] to decide whether to continue or exit the loop. What to do if both >= 0, if both <= 0, else ?
Note that A.sort() has a non neglectable cost, likely O(N*log(N)), it will dominate the cost of the solution you exhibit.
By the way, what is M in the official cost O(N*M^2)?
And the link you provide is another problem (sum all the elements of A or their opposite).

How to build a list of vectors taken from an other list that satisfy a constraint

It's my first try with z3.
I want to find which vectors taken in a list I have to sum to get a given result.
I've try this but that don't compile because R isn't an indice.
Tr_tuple = ((-1,1,0,1,0,0,0,-1),
(1,-1,1,0,0,0,-1,0),
(0,-1,-1,1,0,1,0,0),
(-1,0,1,-1,0,0,0,0),
(0,0,0,-1,-1,1,0,1),
(0,0,-1,0,1,-1,1,0),
(0,1,0,0,0,-1,-1,1),
(1,0,0,0,-1,0,1,-1),
(1,1,-1,-1,1,1,-1,-1),
(-1,-1,1,1,-1,-1,1,1))
Start_tuple = (1,-1,0,-1,0,0,0,1)
depth = 2
G = [Int('g_%s' % i) for i in range(8)]
R = [Int('r_%s' % i) for i in range(depth)]
R_c = [ And (R[i] >= 0, R[i] < 10) for i in range(depth) ]
G_c = [G[i] == Start_tuple[i] + sum([ Tr_tuple[j][i] for j in R]) for i in range(8)]
G_g = [G[i] == 0 for i in range(8)]
I found something but it's like brute force :
M = [[Int('m_%s_%s' % (j,i)) for i in range(8)] for j in range(depth)]
T = [Int('t_%s' % i) for i in range(8)]
M_c = And([
Or([
And([M[j][i] == Tr_tuple[k][i] for i in range(8)])
for k in range(10)])
for j in range(depth)])
G_c = (And([Start_tuple[i]+sum([M[j][i] for j in range(depth)]) == 0 for i in range(8)]))
s = Solver()
s.add(M_c)
s.add(G_c)
if s.check() == sat :
pp(s.model())
else:
print('No solution found')

Explanation for Booth's Algorithm for Lexicographically minimal string rotation

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!

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()))

Something wrong with my PollardP1_rho code but I don't know how to fix it

I tried to use MillerRabin + PollardP1_rho method to factorize an integer into primes in Python3 for reducing time complexity as much as I could.But it failed some tests,I knew where the problem was.But I am a tyro in algorithm, I didn't know how to fix it.So I will put all relative codes here.
import random
def gcd(a, b):
"""
a, b: integers
returns: a positive integer, the greatest common divisor of a & b.
"""
if a == 0:
return b
if a < 0:
return gcd(-a, b)
while b > 0:
c = a % b
a, b = b, c
return a
def mod_mul(a, b, n):
# Calculate a * b % n iterately.
result = 0
while b > 0:
if (b & 1) > 0:
result = (result + a) % n
a = (a + a) % n
b = (b >> 1)
return result
def mod_exp(a, b, n):
# Calculate (a ** b) % n iterately.
result = 1
while b > 0:
if (b & 1) > 0:
result = mod_mul(result, a, n)
a = mod_mul(a, a, n)
b = (b >> 1)
return result
def MillerRabinPrimeCheck(n):
if n in {2, 3, 5, 7, 11}:
return True
elif (n == 1 or n % 2 == 0 or n % 3 == 0 or n % 5 == 0 or n % 7 == 0 or n % 11 == 0):
return False
k = 0
u = n - 1
while not (u & 1) > 0:
k += 1
u = (u >> 1)
random.seed(0)
s = 5 #If the result isn't right, then add the var s.
for i in range(s):
x = random.randint(2, n - 1)
if x % n == 0:
continue
x = mod_exp(x, u, n)
pre = x
for j in range(k):
x = mod_mul(x, x, n)
if (x == 1 and pre != 1 and pre != n - 1):
return False
pre = x
if x != 1:
return False
return True
def PollardP1_rho(n, c):
'''
Consider c as a constant integer.
'''
i = 1
k = 2
x = random.randrange(1, n - 1) + 1
y = x
while 1:
i += 1
x = (mod_mul(x, x, n) + c) % n
d = gcd(y - x, n)
if 1 < d < n:
return d
elif x == y:
return n
elif i == k:
y = x
k = (k << 1)
result = []
def PrimeFactorsListGenerator(n):
if n <= 1:
pass
elif MillerRabinPrimeCheck(n) == True:
result.append(n)
else:
a = n
while a == n:
a = PollardP1_rho(n, random.randrange(1,n - 1) + 1)
PrimeFactorsListGenerator(a)
PrimeFactorsListGenerator(n // a)
When I tried to test this:
PrimeFactorsListGenerator(4)
It didn't stop and looped this:
PollardP1_rho(4, random.randrange(1,4 - 1) + 1)
I have already tested the functions before PollardP1_rho and they work normally,so I know the function PollardP1_rho cannot deal the number 4 correctly,also the number 5.How can I fix that?
I have solved it myself.
There is 1 mistake in the code.
I should not use a var 'result' outside of the function as a global var,I should define in the function and use result.extend() to ensure the availability of the whole recursive process.So I rewrote PollardP1_rho(n, c) and PrimeFactorsListGenerator(n):
def Pollard_rho(x, c):
'''
Consider c as a constant integer.
'''
i, k = 1, 2
x0 = random.randint(0, x)
y = x0
while 1:
i += 1
x0 = (mod_mul(x0, x0, x) + c) % x
d = gcd(y - x0, x)
if d != 1 and d != x:
return d
if y == x0:
return x
if i == k:
y = x0
k += k
def PrimeFactorsListGenerator(n):
result = []
if n <= 1:
return None
if MillerRabinPrimeCheck(n):
return [n]
p = n
while p >= n:
p = Pollard_rho(p, random.randint(1, n - 1))
result.extend(PrimeFactorsListGenerator(p))
result.extend(PrimeFactorsListGenerator(n // p))
return result
#PrimeFactorsListGenerator(400)
#PrimeFactorsListGenerator(40000)
There is an additional tip: You don't need to write a function mod_mul(a, b, n) at all, using Python built-in pow(a, b, n) will do the trick and it is fully optimized.

Resources