I find a new way to traverse the quartet neighbors by using complex number in this solution.
https://leetcode.com/problems/word-search-ii/discuss/59804/27-lines-uses-complex-numbers
(you can just read my example.)
I think it is elegant and concise, but I can not fully understand about it.
Here I have extracted the key code, and simplify the exmaple.
board is a 2d array, and we want to start from every node, and traverse the 4 direction neigbor recursively by dfs:
this is a common way:
def dfs(i, j, word):
# create 4 direction by hand
for I, J in (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1):
# need to check boundary
if 0 <= I < len(board) and 0 <= J < len(board[0]):
dfs(I, J, word + c)
for i, j in board:
dfs(i, j, '')
here is using complex number as index:
board = {i + 1j * j: c
for i, row in enumerate(board)
for j, c in enumerate(row)}
def dfs(z, word):
c = board.get(z)
# here is visit 4 direction neighbors, which I don't understand
if c:
for k in range(4):
search(node[c], z + 1j ** k, word + c)
for z in board:
dfs(z, '')
I think there is two advantages by using complex number:
don't need to create 4 direction by hand
don't need to check boundary
But I can't understand here for k in range(4): dfs(z + 1j ** k, word + c)
can somebody explain this algorithm? really appreciate it.
If what I think is correct this solution uses the following property of the imaginary number j:
which if added to a complex number as a representation of a grid are the nodes: right, up, left, down
Related
I was trying out longest palindromic subsequence problem from leetcode.
One of the discussed solution is as follows:
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
dp[i][i] = 1
for j in range(i+1, n):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
So it starts from end of the string:
I was guessing if it is possible to begin from the starting of the string. That is if its possible to have loops something like this:
for i in range(0, n):
for j in range(i+1, n):
# ...
But dp[i + 1] wont be calculated for any given iteration of i and we need dp[i+1] for evaluating
dp[i][j] = dp[i + 1][j - 1] + 2 and
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
Is it possible to change these two updates to dp (and hence come up with new recurrence relation) in some way to make it possible to begin from the starting of the string or starting from the end of the string is the only way possible !? (I was not able to come up with any recurrence solution / index adjustments to make it possible. So I have started to believe that its indeed not possible. But I wanted to be sure.)
The first hint that you can do this from the beginning is that let's say you're given a string 'baabbcc' that this logic gets the answer for, the same logic will work for the reversed string as well ('ccbbaab').
The more robust reasoning for this can be derived from what dp[i][j] represents. The value represents the Longest Palindromic Subsequence between i and j inclusive. We calculate this dp array using two pointers, say i and j.
We iterate over all possible values of i and j, and if s[i] == s[j] then we know that the answer from i to j will be equal to the answer for i+1 to j-1 + 2 because we can take the answer from i+1 to j-1 and add s[i] and s[j] to the beginning and end of that. I hope this is clear from the code you provided.
What that means is that to calculate dp[i][j], you need dp[i+1][j-1].
The code you have provided does this by starting the i pointer from the ending and for every i, it loops from j = i till j = n-1. This means that i+1 is reached before i and j-1 is reached before j.
However, you can achieve the same effect starting from the beginning. This time, start by moving the j pointer from the beginning, and for every j, move the i pointer backward from i = j till i = 0. This ensures that j-1 is reached before j and i+1 is reached before i, which is what we're looking for.
The final code would look something like this (Which I've submitted and gotten accepted):
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
for j in range(0, n):
dp[j][j] = 1
for i in range(j-1, -1, -1):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
I am working on the problem https://leetcode.com/problems/k-closest-points-to-origin/ with the question statement reproduced here:
Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest (Euclidean distance) points to the origin (0, 0). The k closest points may be returned in any order.
I am using the QuickSelect algorithm for this purpose. Here is my current, working but slow code using the Lomuto partitioning scheme (taking the rightmost element to be the pivot).
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
# attempt at using quickselect
def dist(point):
a, b = point
return a ** 2 + b ** 2
def quickSelect(l, r):
# Using the (slower) Lomuto Partioning Scheme
pivot, p = points[r], l
for i in range(l, r):
if dist(points[i]) <= dist(pivot):
points[p], points[i] = points[i], points[p] # swap them
p += 1
points[p], points[r] = points[r], points[p]
# if the pointer's index is greater than the desired index k,
# then we need to narrow the range
if p == k - 1: return points[:k]
elif p < k - 1: return quickSelect(p + 1, r)
else: return quickSelect(l, p - 1)
return quickSelect(0, len(points) - 1)
Here is my attempt at replacing Lomuto with Hoare.
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
# attempt at using quickselect
def dist(point):
a, b = point
return a ** 2 + b ** 2
def quickSelect(l, r):
# Using the (faster) Hoare scheme
pivot_index = ((r + l) // 2)
pivot = points[pivot_index]
i, j = l - 1, r + 1
while True:
while True:
i += 1
if dist(points[i]) >= dist(pivot):
break
while True:
j -= 1
if dist(points[j]) <= dist(pivot):
break
if i >= j:
p = j
break
points[i], points[j] = points[j], points[i]
# if the pointer's index is greater than the desired index k,
# then we need to narrow the range
if p == k - 1: return points[:k]
elif p < k - 1: return quickSelect(p + 1, r)
else: return quickSelect(l, p - 1)
return quickSelect(0, len(points) - 1)
However, it seems as though this replacement has gone awry. The following test case is failing with my Hoare attempt:
points = [[-95,76],[17,7],[-55,-58],[53,20],[-69,-8],[-57,87],[-2,-42],[-10,-87],[-36,-57],[97,-39],[97,49]]
5
k = 5
My output is [[-36,-57],[17,7],[-69,-8],[53,20],[-55,-58]] while the expected output is [[17,7],[-2,-42],[53,20],[-36,-57],[-69,-8]].
With Hoare partition scheme, the pivot and elements equal to the pivot can end up anywhere, and after a partition step p is not an index to the pivot, but just a separator, values to the left or at p are <= pivot, values to the right of p are >= pivot. With Hoare partition scheme quickselect requires recursing to the the base case of 1 element in order to find the kth element. If there are other elements equal to the kth element, they could end up on either side or both sides of the kth element.
Okay so we just got introduced to loops, and I've been scratching my head for days trying to figure it out. I'm trying to print pascal's triangle.
I found some code online that is simple and something that I can really understand!
rows = int(input("Enter the number of rows : "))
for i in range(0, rows):
coff = 1
for j in range(1, rows-i):
print(" ", end="")
for k in range(0, i+1):
print(" ", coff, end="")
coff = int(coff * (i - k) / (k + 1))
print()
However, how did they get this formula? coff = int(coff * (i - k) / (k + 1)),
I've been searching online on how they derived that, but I still can't find any answers.
I understand how the loop works but I'm scratching my head as to how they got the formula to get the terms. Help is appreciated!
For each position in Pascal's Triangle, the value is i Choose k, where i is the row (starting with 0 on the first row) and k is the position in the row (also starting with 0). i and k here (almost) matches with the variables in your code.
Now, Wikipedia sports this identity:
The trick for transforming this into your code, is that coff = int(coff * (i - k) / (k + 1)) runs after coff is printed, so we need to add 1 to k.
If you want the code closer to the formula, you could do the following:
coff = 1
for k in range(1, i+2):
print(" ", coff, end="")
coff = int(coff * (i + 1 - k) / k)
I'm having trouble with a dynamic programming problem. I have tested my solution against test cases, and it is correct. However, it is too slow. This leads me to believe that I may not be caching the solutions to subproblems effectively. Here is the problem statement:
There is an array A that contains N numbers. Given an array, a player can split the array into two non-empty subarrays only if the elements in each subarray sum to the same value. After a split is made, the player discards one subarray and is allowed to continue splitting on the remaining subarray. This continues until a split is no longer possible. What is the maximum number of splits possible on the given array A?
Here is my (slow) solution, which calls topSplit(A) on the given array:
def topSplitAux(A, C, i, j):
if -1 != C[i][j]:
return C[i][j]
if i == j:
return 0
s = float('-inf')
for k in range(i + 1, j):
if sum(A[i:k]) == sum(A[k:j]):
p1 = 1 + topSplitAux(A, C, i, k)
p2 = 1 + topSplitAux(A, C, k, j)
s = max(s, p1, p2)
C[i][j] = s
if s == float('-inf'): # we couldn't split, game over
C[i][j] = 0
return C[i][j]
def topSplit(A):
# initialize a cache to store solutions already solved
n = len(A)
# the subproblem we are interested in will be in C[0][n]
C = [[-1 for _ in range(n + 1)] for _ in range(n + 1)]
return topSplitAux(A, C, 0, n)
if __name__ == '__main__':
T = int(raw_input())
for t in range(T):
N = int(raw_input())
A = map(int, raw_input().split())
n = len(A)
print topSplit(A)
Here's a simple test case:
3
3
3 3 3
4
2 2 2 2
7
4 1 0 1 1 0 1
with expected result:
0
2
3
Any help on making this solution faster would be greatly appreciated. Thanks!
I wrote both a brute-force and a divide-and-conquer implementation of the Max Subarray problem in Python. Tests are run by drawing a random sample of integers.
When the length of the input array is large, the assert in __main__ fails because the recursive algorithm does not return the correct answer. However, the two algorithms DO agree when the array is less than 10 elements long (this is approximate, and the actual size of the failed input varies on each execution). The issue does not seem to be related to even or odd array lengths, but it does appear to be related to how the array is indexed.
Sorry if I'm missing something stupid, but why does the recursive algorithm stop returning the correct output when the input array starts getting larger?
# Subarray solutions are represented by an array in the form
# [lower_bound, higher_bound, sum]
from sys import maxsize
import random
import time
# Brute force implementation (THETA(n^2))
def bf_max_subarray(A):
biggest = -maxsize - 1
left = 0
right = 0
for i in range(0, len(A)):
sum = 0
for j in range(i, len(A)):
sum += A[j]
if sum > biggest:
biggest = sum
left = i
right = j
return [left, right, biggest]
# Part of divide-and-conquer solution
def cross_subarray(A, l, m, r):
lsum = -maxsize - 1
rsum = -maxsize - 1
lbound = 0
rbound = 0
tempsum = 0
for i in range(m, l-1, -1):
tempsum += A[i]
if tempsum > lsum:
lsum = tempsum
lbound = i
tempsum = 0
for j in range(m+1, r+1):
tempsum += A[j]
if tempsum > rsum:
rsum = tempsum
rbound = j
return [lbound, rbound, lsum + rsum]
# Recursive solution
def rec_max_subarray(A, l, r):
# Base case: array of one element
if (l == r):
return [l, r, A[l]]
else:
m = (l+r)//2
left = rec_max_subarray(A, l, m)
right = rec_max_subarray(A, m+1, r)
cross = cross_subarray(A, l, m, r)
# Returns the array representing the subarray with the maximum sum.
return max([left, right, cross], key=lambda i:i[2])
if __name__ == "__main__":
for i in range(1, 101):
A = random.sample(range(-i*2, i), i)
start = time.clock()
bf = bf_max_subarray(A)
bf_time = time.clock() - start
start = time.clock()
dc = rec_max_subarray(A, 0, len(A)-1)
dc_time = time.clock() - start
assert dc == bf # Make sure the algorithms agree.
The subarray with the maximum sum is represented by an array of the form [left_bound, right_bound, sum].
But thanks toreturn max([left, right, cross], key=lambda i:i[2]), rec_max_subarray returns the correct maximum sum for A, but risks returning indicies that do not match the indicies returned in bf_max_subarray. My error was assuming that the boundaries of a subarray with the maximum sum would be unique.
The solution is to either fix the criteria that selects a subarray, or just to assert the equality of the sums using assert dc[2] == bf[2].