minimum jump to reach to end - python

Question statement:
Given array: [1,0,1,0,1,1,1,1,1,0,1,1,1,0]
output: minimum steps required to reach to end
Conditions:
step on 0 is exit
you can take max of 1 step or 2 steps at a time.
I have done using without DP, Is a DP solution present for this problem.
My code:
def minjump(arr):
n = len(arr)
if n <= 0 or arr[0] == 0:
return 0
index = jump = 0
while index < n:
if index == n-1:
return jump
if arr[index] == 0 and arr[index+1] == 0:
return 0
if arr[index] == 1:
if index < n-2 and arr[index+2] == 1:
jump +=1
index +=2
else:
jump += 1
index += 1
return jump

A naΓ―ve solution with no memoization simply recurses through the list taking both one or two steps and retaining the minimum steps needed:
def min_steps(array, current_count, current_position):
if current_position >= len(array): # Terminal condition if you've reached the end
return current_count
return (
min(
min_steps(array, current_count + 1, current_position + 1),
min_steps(array, current_count + 1, current_position + 2),
) # minimum count after taking one step or two
if array[current_position] # if current step is valid (non-zero)
else float("inf") # else, return float.infinity (fine since we're not imposing types on this prototype)
)
def minjump(arr):
result = min_steps(arr, 0, 0)
return 0 if result == float("inf") else result

You can solve a more general problem using dp, in the pseudocode bellow k represents the maximum number of steps you can jump:
int n = arr.Length
int dp[n]
for (i = 0 ; i < n; i++) {
int lowerBound = max(i - k, 0)
for (j = i - 1; j >= lowerBound; j--) {
if (arr[j] == 1 && (j == 0 || dp[j] > 0)) {
dp[i] = min(dp[i], 1 + dp[j])
}
}
}
return dp[n - 1]

Related

Optimising Performance of Codility Flags Python

I've written the below algorithm as a solution to Codility Flags. This passes the correctness checks, however it times out on most of the performance checks.
The complexity of this should be O(m**2) where m is the number of peaks in A and n is the length of A. However, the while potentialK > maxFlags loop should only execute until a suitable number of flags is found which satisfies the criteria. I'm not sure how to further optimise this for time complexity.
def solution(A):
peaks = []
distances = []
if len(A) == 1: return 0
for i in range(1, len(A) -1):
if A[i] > A[i-1] and A[i] > A[i+1]:
if len(distances) == 0:
distances.append(i)
else:
distances.append(i - peaks[-1])
peaks.append(i)
if len(peaks) == 0: return 0
if len(peaks) == 1: return 1
if len(peaks) == 2: return 2 if peaks[1] - peaks[0] >= 2 else 1
potentialK = len(peaks)
maxFlags = 0
while potentialK > maxFlags:
cumDistance = 0
flags = 0
firstFlag = True
for i in range(1, len(distances)):
cumDistance += distances[i]
if cumDistance >= potentialK:
if firstFlag:
flags += 2
firstFlag = False
else:
flags += 1
cumDistance = 0
if flags > maxFlags and flags == potentialK:
return flags
elif flags > maxFlags and potentialK > flags:
maxFlags = flags
potentialK -= 1
return maxFlags
Your algorithm is O(n^2), since there can be O(n) peaks in the input. Speeding up your algorithm relies on the fact that you know the input size in advance.
Observe that the answer is an integer in the interval [1, ceil(sqrt(n))]. Any distance requirement that's less than 1 means that you can't place any flags. You can't place more than ceil(sqrt(n)) flags because of the distance requirement, even if every element was somehow a peak (which isn't possible).
So one optimization you could make is that you need to only check for O(sqrt(n)) potentialK values. (You posted this as an answer to your own question.) That would bring the runtime down to O(n^(3/2)), since m is O(n), which is apparently fast enough to pass Codility's tests, but I think that the runtime can still be improved (and so can the correctness).
We can make one more observation:
There exists a positive integer i such that:
for every j, such that j is a positive integer less than i, we can place j flags that are at least j distance apart, and
for every j, such that j is a positive integer greater than i, we cannot place j flags that are at least j distance apart.
This enables us to use binary search:
import math
def does_distance_work(peaks, distance):
peak_count = 1
last_peak = peaks[0]
for i in range(len(peaks)):
if peaks[i] >= last_peak + distance:
peak_count += 1
last_peak = peaks[i]
return peak_count >= distance
def solution(A):
# Get the indices of the peaks.
peaks = []
for i in range(1, len(A) - 1):
if A[i] > A[i - 1] and A[i] > A[i + 1]:
peaks.append(i)
# Return 0 if no peaks.
if not peaks:
return 0
# Check maximum value.
if does_distance_work(peaks, math.ceil(math.sqrt(len(A)))):
return math.ceil(math.sqrt(len(A)))
# If neither of the above two checks apply, find the largest i (as specified above) using binary search.
low, high = 1, math.ceil(math.sqrt(len(A))) - 1
while low <= high:
mid = low + (high - low) // 2
mid_valid_distance = does_distance_work(peaks, mid)
mid_plus_one_valid_distance = does_distance_work(peaks, mid + 1)
if not mid_valid_distance:
high = mid
elif mid_plus_one_valid_distance:
low = mid + 1
else:
return mid
# If we've reached this line, something has gone wrong.
return -1
which recurses to a depth of O(log(sqrt(n)), with O(n) work for each iteration of our binary search. Then the final runtime is O(n * log(sqrt(n))), which should (and does) pass the performance tests.
I managed to optimize it as follows:
Since the distance between the individual flags has to be >= the number of flags, we know the maximum number of flags will be the root of the last element of peaks - the first element of peaks: sqrt(peaks[-1] - peaks[0])
I was then able to update the initial value of potentialK to
potentialK = math.ceil(math.sqrt(peaks[-1] - peaks[0]))
which should substantially reduce the number of iterations of the outer while loop.
import math
def solution(A):
peaks = []
distances = []
if len(A) == 1: return 0
for i in range(1, len(A) -1):
if A[i] > A[i-1] and A[i] > A[i+1]:
if len(distances) == 0:
distances.append(i)
else:
distances.append(i - peaks[-1])
peaks.append(i)
if len(peaks) == 0: return 0
if len(peaks) == 1: return 1
if len(peaks) == 2: return 2 if peaks[1] - peaks[0] >= 2 else 1
potentialK = math.ceil(math.sqrt(peaks[-1] - peaks[0]))
maxFlags = 0
while potentialK > maxFlags:
cumDistance = 0
flags = 0
firstFlag = True
for i in range(1, len(distances)):
cumDistance += distances[i]
if cumDistance >= potentialK:
if firstFlag:
flags += 2
firstFlag = False
else:
flags += 1
cumDistance = 0
if flags > maxFlags and flags == potentialK:
return flags
elif flags > maxFlags and potentialK > flags:
maxFlags = flags
potentialK -= 1
return maxFlags

Car Fueling Problem by Greedy Alogorithm (getting list index out of range)

I have a small problem solving the Car fueling problem using the Greedy Algorithm.
Problem Introduction
You are going to travel to another city that is located 𝑑 miles away from your home city. Your car can travel
at most π‘š miles on a full tank and you start with a full tank. Along your way, there are gas stations at distances stop1 stop2 . . . ,stopN from your home city. What is the minimum number of refills needed?
Input:
950
400
4
200 375 550 750
Output:
2
What I've tried as of now
def car_fueling(dist,miles,n,gas_stations):
num_refill, curr_refill, last_refill = 0,0,0
while curr_refill <= n:
last_refill = curr_refill
while (curr_refill <= n-1) & (gas_stations[curr_refill + 1] - gas_stations[last_refill] <= miles):
curr_refill += 1
if curr_refill == last_refill:
return -1
if curr_refill <= n:
num_refill += 1
return num_refill
What is the problem I'm facing
In the statement
while (curr_refill <= n-1) & (gas_stations[curr_refill + 1] - gas_stations[last_refill] <= miles)
I am getting the error IndexError: list index out of range. It is because of gas_stations[curr_refill + 1]. So when I try to separate it as a while loop and an if statement as in
while (curr_refill <= n-1):
if (gas_stations[curr_refill + 1] - gas_stations[last_refill] <= miles):
curr_refill += 1
else:
break
It is entering an infinite loop.
Can you kindly point out the mistake I'm facing?
Thanks in advance.
A few issues:
& is not the boolean and-operator. Use and
curr_refill + 1 can be n, and hence produce the error you got. Note that the distance after the last gas station can be determined using dist
The value of last_refill is wrong from the start: you did not refill (yet) at station 0, so it should not be initialised as 0. Instead use another variable that represents how far you can currently drive.
Corrected code:
def car_fueling(dist,miles,n,gas_stations):
num_refill, curr_refill, limit = 0,0,miles
while limit < dist: # While the destination cannot be reached with current fuel
if curr_refill >= n or gas_stations[curr_refill] > limit:
# Cannot reach the destination nor the next gas station
return -1
# Find the furthest gas station we can reach
while curr_refill < n-1 and gas_stations[curr_refill+1] <= limit:
curr_refill += 1
num_refill += 1 # Stop to tank
limit = gas_stations[curr_refill] + miles # Fill up the tank
curr_refill += 1
return num_refill
# Test cases
print(car_fueling(950, 400, 4, [200, 375, 550, 750])) # 2
print(car_fueling(10, 3, 4, [1, 2, 5, 9])) # -1
probably this will fix the index error. The logic of your code is correct and that is the same as what I did in the code in the block of else statement. Add the start point and end point(total distance) can avoid index error. First check the total distance to see if it can be arrived in a full tank. If not do the else statement.
def compute_min_refills(distance, tank, stops):
numrefill, currentrefill= 0,0
stops = [0] + stops + [distance] #include the start and end points in the stops list
if distance <= tank:
return 0
else:
while currentrefill < len(stops)-1:
lastrefill = currentrefill
#print(currentrefill, lastrefill, len(stops))
while currentrefill < len(stops)-1 and stops[currentrefill+1] - stops[lastrefill]<=tank:
currentrefill += 1
if currentrefill == lastrefill:
return -1
if currentrefill < len(stops)-1:
numrefill +=1
#print(numrefill)
return numrefill
if __name__ == '__main__':
#print(compute_min_refills(10, 3, [1,2,5,9]))
It's in an infinite loop because n is not being incremented.
Increment n where it makes the most sense (for example, at the end of your while statement).
def car_fueling(dist,miles,n,gas_stations):
num_refill, curr_refill, last_refill = 0,0,0
while curr_refill <= n:
last_refill = curr_refill
while (curr_refill <= n-1) & (gas_stations[curr_refill + 1] - gas_stations[last_refill] <= miles):
curr_refill += 1
if curr_refill == last_refill:
return -1
if curr_refill <= n:
num_refill += 1
n+=1 # Increment
return num_refill
def car_refill(dist,cap,n,stops):
stops.insert(0,0)
stops.append(dist)
num_refill,curr_refill = 0,0
while curr_refill <= n:
last_refill = curr_refill
while (curr_refill <= n and stops[curr_refill + 1] - stops[last_refill] <= cap):
curr_refill += 1
if curr_refill == num_refill :
return -1
if curr_refill <= n:
num_refill +=1
return num_refill
try this ....
I dont know why but answers seems overly complicated i just imagined myself driving and cameup with this simple solution
function minfill(distance, miles, n, stations) {
//added final distance to last station for simplicity can simply push to array.
stations = [...stations, distance]
let refill = 0,
limit = miles,
dt = 0, //distance travelled
current = 0; //current station
while (current <= n) {
//check if next or first station is unreachable
if ((Math.abs(stations[current] - stations[current + 1]) > limit) || stations[0] > limit) return -1
//check if we need to refuel or pass
if (Math.abs(dt - stations[current]) <= limit) {
current++
}
//if next distance was over limit we set distance tavelled to previous station ,current station was already pointed to next in above block
else {
dt = stations[current - 1]
refill++
}
}
return refill
}
p.s-this code is written in node/javascript, though i have passed all test for this question but i know here there are smarter people who will help to improve/correct this code or provide some pointers.
import java.util.;
import java.io.;
public class CarFueling {
static int compute_refills(int dist, int tank, int stops[], int n) {
int current_refills=0;
int num_refills=0;
int last_refill=0;
while(current_refills<=n) {
last_refill = current_refills;
while ((current_refills !=stops.length-1) && (stops[current_refills + 1] - stops[last_refill]) <= tank) {
current_refills +=1 ;
}
if (current_refills == last_refill)
return -1;
if (current_refills <= n)
num_refills +=1;
}
return num_refills;
}
public static void main (String[]args){
Scanner scanner = new Scanner(System.in);
int dist = scanner.nextInt();
int tank = scanner.nextInt();
int n = scanner.nextInt();
int stops[] = new int[n * n * n];// to solve array index out of bound exception increase the size of the array
for (int i = 0; i < n; i++) {
stops[i] = scanner.nextInt();
}
System.out.println(compute_refills(dist, tank, stops, n));
}
}
The answer that add [0] and [d] to stops should work, but current_refill comparison should be current_refill < len(stops)- 2 everywhere, because two stops are added.
There's another way to solve this problem.
def compute_min_number_of_refills(d, m, stops):
if d <= m:
return 0
total_refill = 0
last_refill = -1
limit = m
stops.append(d)
i = 0
while i < len(stops):
if stops[i] >= limit:
current_refill = i - 1 if stops[i] > limit else i
if current_refill == last_refill:
return -1
last_refill = current_refill
total_refill += 1
limit = m + stops[current_refill]
i = current_refill + 1
else:
i += 1
return total_refill
I used this code and got the correct answer from a course in Coursera.
# python3
import sys
def compute_min_refills(distance, tank, stops):
capacity_tank = tank
refill = 0
if capacity_tank >= distance:
return 0
if capacity_tank < stops[0] or (distance-stops[-1]) > capacity_tank:
return -1
for i in range(1, len(stops)):
if (stops[i]-stops[i-1]) > capacity_tank:
return -1
if stops[i] > tank:
tank = (stops[i-1] + capacity_tank)
refill += 1
if distance > tank:
refill += 1
return refill
if __name__ == '__main__':
d, m, _, *stops = map(int, sys.stdin.read().split())
print(compute_min_refills(d, m, stops))

Target sum dp algorithm when element can be zero

Target sum prompt:
You are given a set of positive numbers and a target sum β€˜S’. Each number should be assigned either a β€˜+’ or β€˜-’ sign. We need to find the total ways to assign symbols to make the sum of the numbers equal to the target β€˜S’.
Input: {1, 1, 2, 3}, S=1
Output: 3
Explanation: The given set has '3' ways to make a sum of '1': {+1-1-2+3} & {-1+1-2+3} & {+1+1+2-3}
let’s say β€˜Sum(s1)’ denotes the total sum of set β€˜s1’, and β€˜Sum(s2)’ denotes the total sum of set β€˜s2’. Add negative sign to set 's2'
This equation can be reduced to the subset sum problem target + sum(nums)/2
sum(s1) - sum(s2) = target
sum(s1) + sum(s2) = sum(nums)
2 * sum(s1) = target + sum(nums)
sum(s1) = target + sum(nums) / 2
def findTargetSumWays(nums, S):
"""
:type nums: List[int]
:type S: int
:rtype: int
"""
if (sum(nums) + S) % 2 == 1 or sum(nums) < S:
return 0
ssum = (sum(nums) + S) // 2
dp = [[0 for _ in range(ssum + 1)] for _ in range(len(nums))]
# col == 0
for i in range(len(nums)):
# [] or [0]
if i == 0 and nums[i] == 0:
dp[i][0] = 2
# [] or [0] from previous
elif nums[i] == 0:
dp[i][0] = 2 * dp[i-1][0]
else: # empty set only
dp[i][0] = 1
# take 1st element nums[0] in s == nums[0]
for s in range(1, ssum + 1):
if nums[0] == s:
dp[0][s] = 1
for i in range(1, len(nums)):
for s in range(1, ssum + 1):
if nums[i] != 0:
# skip element at i
dp[i][s] = dp[i - 1][s]
# include element at i
if s >= nums[i]:
dp[i][s] += dp[i - 1][s - nums[i]]
else: # nums[i] = 0
dp[i][s] = dp[i-1][s] * 2
return dp[len(nums) - 1][ssum]
I've spent a few hours on this prompt but still couldn't pass the following example
[7,0,3,9,9,9,1,7,2,3]
6
expected: 50
output: 43 (using my algorithm)
I've also looked through other people's answers here, they all makes sense but I just want to know where could I have possibly missed in my algorithm here?
You can do it like this:
from itertools import product
def findTargetSumWays(nums, S):
a = [1,-1]
result=[np.multiply(nums,i) for i in list(product(a, repeat=len(nums))) if sum(np.multiply(nums,i))==S]
return(len(result))
findTargetSumWays(inputs,6)
50
Basically I get all possible combinations of -1,1 in tuples with the size the same as input elements and then I'm multiplying these tuples with input.
I ran into this same issue when handling zeroes but I did this on C++ where I handled zeroes seperately.
Make sure that in the knapsack approach skip zeroes i.e.
if(a[i-1] == 0)
dp[i][j] = dp[i-1][j];
We can handle zeroes seperately by simply counting the zero occurences and we can put them in either S1 or S2. So, for each zero it is 2*(answer) and for n zeroes its 2^n * (answer) i.e.
answer = pow(2, num_zero) * answer;
Also, don't forget to simply return zero if sum(nums) + target is odd as S1 can't be fractional or target is greater than sum(nums) i.e.
if(sum < target || (sum+target)%2 == 1)
return 0;
The overall code looks like this:
int subsetSum(int a[], int n, int sum) {
int dp[n+1][sum+1];
for(int i = 0; i<sum+1; i++)
dp[0][i] = 0;
for(int i = 0; i<n+1; i++)
dp[i][0] = 1;
for(int i = 1; i<n+1; i++) {
for(int j = 1; j<sum+1; j++) {
if(a[i-1] == 0)
dp[i][j] = dp[i-1][j];
else if(a[i-1]<=j)
dp[i][j] = dp[i-1][j-a[i-1]] + dp[i-1][j];
else
dp[i][j] = dp[i-1][j];
}
}
return dp[n][sum]; }
int findTargetSumWays(int a[], int target) {
int sum = 0;
int num_zero = 0;
for(int i = 0; i<a.size(); i++) {
sum += a[i];
if(a[i] == 0)
num_zero++;
}
if(sum < target || (sum+target)%2 == 1)
return 0;
int ans = subsetSum(a, a.size(), (sum + target)/2);
return pow(2, num_zero) * ans;
}
The source of the problem is this part, initializing col == 0:
# col == 0
for i in range(len(nums)):
# [] or [0]
if i == 0 and nums[i] == 0:
dp[i][0] = 2
# [] or [0] from previous
elif nums[i] == 0:
dp[i][0] = 2 * dp[i-1][0]
else: # empty set only
dp[i][0] = 1
This code treats zeros differently depending on how the list is ordered (it resets the value to 1 if it hits a nonzero value). It should instead look like this:
# col == 0
for i in range(len(nums)):
# [] or [0]
if i == 0 and nums[i] == 0:
dp[i][0] = 2
elif i == 0:
dp[i][0] = 1
# [] or [0] from previous
elif nums[i] == 0:
dp[i][0] = 2 * dp[i-1][0]
else: # empty set only
dp[i][0] = dp[i - 1][0]
This way, the first value is set to either 2 or 1 depending on whether or not it's zero, and nonzero values later in the list don't reset the value to 1. This outputs 50 in your sample case.
You can also remove room for error by giving simpler initial conditions:
def findTargetSumWays(nums, S):
"""
:type nums: List[int]
:type S: int
:rtype: int
"""
if (sum(nums) + S) % 2 == 1 or sum(nums) < S:
return 0
ssum = (sum(nums) + S) // 2
dp = [[0 for _ in range(ssum + 1)] for _ in range(len(nums) + 1)]
# col == 0
dp[0][0] = 1
for i in range(len(nums)):
for s in range(ssum + 1):
dp[i + 1][s] = dp[i][s]
if s >= nums[i]:
dp[i + 1][s] += dp[i][s - nums[i]]
return dp[len(nums)][ssum]
This adds an additional row to represent the state before you add any numbers (just a 1 in the top left corner), and it runs your algorithm on the rest of the rows. You don't need to initialize anything else or treat zeros differently, and this way it should be easier to reason about the code.
The issue with your function is related to the way you manage zero values in the list. Perhaps a simpler way for you to handle the zero values would be to exclude them from the process and then multiply your resulting count by 2**Z where Z is the number of zero values.
While trying to find the problem, I did a bit of simplification on your code and ended up with this: (which gives the right answer, even with zeroes in the list).
ssum = (sum(nums) + S) // 2
dp = [1]+[0]*ssum # number of sets that produce each sum from 0 to ssum
for num in nums:
for s in reversed(range(num,ssum + 1)):
dp[s] += dp[s-num]
return dp[ssum]
What I did was:
Eliminate a dimension in dp because you don't need to keep all the previous set counts. Only the current and next one. Actually it can work using only the current set counts if you process the sum values backwards from ssum down to zero (which i did).
The condition s >= nums[i]was eliminated by starting the s range from the current num value so that the index s - num can never be negative.
With that done, there was no need for an index on nums, I could simply go through the values directly.
Then I got rid of all the special conditions on zero values by initializing dp with 1 for the zero sum (i.e. initially an empty set is the one solution to obtain a sum of zero, then increments proceed from there).
Starting with the empty set baseline allows the progressive accumulation of set counts to produce the right result for all values without requiring any special treatment of zeroes. When num is zero it will naturally double all the current set counts because dp[s] += dp[s-0] is the same as dp[s] = 2 * dp[s]. If the list starts out with a zero then the set count for a sum of zero (dp[0]) will be doubled and all subsequent num values will have a larger starting count (because they start out from the dp[0] value initialized with 1 for the empty set).
With that last change, the function started to give the right result.
My assertion is that, because your solution was not starting from the "empty set" baseline, the zero handling logic was interfering with the natural progression of set counts. I didn't try to fine tune the zero conditions because they weren't needed and it seemed pointless to get them to arrive at the same states that a mere initialization "one step earlier" would produce
From there, the logic can be further optimized by avoiding assignments do dp[s] outside the range of minimum and maximum possible sums (which "slides" forward as we progress through the nums list):
ssum = (sum(nums) + S) // 2
dp = [1]+[0]*ssum
maxSum = 0
minSum = S - ssum # equivalent to: ssum - sum(nums)
for num in nums:
maxSum += num
minSum += num
for s in reversed(range(max(num,minSum),min(ssum,maxSum)+1)):
dp[s] += dp[s-num]
return dp[ssum]

3SUM (finding all unique triplets in a list that equal 0)

I am working on the 3SUM problem (taken from leetcode), which takes a list as input and finds all unique triplets in the lists such that a+b+c=0. I am not really sure what my code is doing wrong, but it currently returns an empty list for this list [-1, 0, 1, 2, -1, -4], so it is not recognizing any triplets that sum to 0. I would appreciate any suggestions or improved code.
Here's my code:
result = []
nums.sort()
l = 0
r=len(nums)-1
for i in range(len(nums)-2):
while (l < r):
sum = nums[i] + nums[l] + nums[r]
if (sum < 0):
l = l + 1
if (sum > 0):
r = r - 1
if (sum == 0):
result.append([nums[i],nums[l],nums[r]])
print(result)
A couple things to note.
Don't use sum as a variable name because that's a built-in function.
Your indexing is a little problematic since you initialize l = 0 and have i begin at 0 as well.
Don't rest on your laurels: increment the value of l when you find a successful combination. It's really easy to forget this step!
An edited version of your code below.
nums = [-1, 0, 1, 2, -1, -4]
result = []
nums.sort()
r=len(nums)-1
for i in range(len(nums)-2):
l = i + 1 # we don't want l and i to be the same value.
# for each value of i, l starts one greater
# and increments from there.
while (l < r):
sum_ = nums[i] + nums[l] + nums[r]
if (sum_ < 0):
l = l + 1
if (sum_ > 0):
r = r - 1
if not sum_: # 0 is False in a boolean context
result.append([nums[i],nums[l],nums[r]])
l = l + 1 # increment l when we find a combination that works
>>> result
[[-1, -1, 2], [-1, 0, 1], [-1, 0, 1]]
If you wish, you can omit the repeats from the list.
unique_lst = []
[unique_lst.append(sublst) for sublst in result if not unique_lst.count(sublst)]
>>> unique_lst
[[-1, -1, 2], [-1, 0, 1]]
Another approach uses itertools.combinations. This doesn't require a sorted list.
from itertools import combinations
result = []
for lst in itertools.combinations(nums, 3):
if sum(lst) == 0:
result.append(lst)
A nested for loop version. Not a big fan of this approach, but it's basically the brute-force version of the itertools.combinations solution. Since it's the same as approach as above, no sort is needed.
result = []
for i in range(0, len(nums)-2):
for j in range(i + 1, len(nums)-1):
for k in range(j + 1, len(nums)):
if not sum([nums[i], nums[j], nums[k]]): # 0 is False
result.append([nums[i], nums[j], nums[k]])
Uncomment print statement from my solution:
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
# print('Input: {}'.format(nums))
nums.sort() # inplace sorting, using only indexes
N, result = len(nums), []
# print('Sorted Input: {}'.format(nums))
for i in range(N):
if i > 0 and nums[i] == nums[i-1]:
# print("Duplicate found(when 'i' iterate ) at index: {}, current: {}, prev: {}, so JUMP this iteration------".format(i,nums[i], nums[i-1]))
continue
target = nums[i]*-1
s,e = i+1, N-1
# print('~'*50)
# print("Target: {} at index: {} & s: {} e: {} {}".format(target,i, s, e, '----'*2))
while s<e: # for each target squeeze in s & e
if nums[s]+nums[e] == target:
result.append([nums[i], nums[s], nums[e]])
# print(' {} + {} == {}, with s: {} < e: {}, Triplet: {}, MOVING --> R'.format(nums[s], nums[e], target,s, e,result))
s = s+1
while s<e and nums[s] == nums[s-1]: # duplicate
# print("Duplicate found(when 's' iterates) at s: {} < e: {}, WILL KEEP MOVING ---> R (s: {}) == (s-1: {})".format(s, e, nums[s], nums[s - 1]))
s = s+1
elif nums[s] + nums[e] < target:
# print(' {} + {} < {}, with s: {} e: {}, MOVING ---> R'.format(nums[s], nums[e], target,s, e))
s = s+1
else:
# print(' {} + {} > {}, with s: {} e: {}, MOVING <--- L'.format(nums[s], nums[e], target,s, e))
e = e-1
return result
It will help you to understand the algorithm better. Also, this algorithm is 3 times faster than the above available options. It takes ~892.18 ms compared to the above alternative with runs in ~4216.98 ms time. The overhead is because of the additional removal of duplicates logic.
I did a similar approach as 3novak, but I added in the case where the number list is less than three integers returning an empty list.
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
# if less than three numbers, don't bother searching
if len(nums) < 3:
return []
# sort nums and use current sum to see if should have larger number or smaller number
nums = sorted(nums)
triplets = []
for i in range(len(nums)-2): # i point to first number to sum in list
j = i + 1 # j points to middle number to sum in list
k = len(nums) - 1 # k points to last number to sum in list
while j < k:
currSum = nums[i] + nums[j] + nums[k]
if currSum == 0:
tmpList = sorted([nums[i], nums[j], nums[k]])
if tmpList not in triplets:
triplets.append(tmpList)
j += 1 # go to next number to avoid infinite loop
# sum too large, so move k to smaller number
elif currSum > 0:
k -= 1
# sum too small so move j to larger number
elif currSum < 0:
j += 1
return triplets
I'm doing the same problem at leetcode, but still have a runtime error. This may be able to be done by using a binary search tree-like algorithm to find the third result, as well.
Using two pointer approach:
First sort the list.
Iterate from left to right. Say current position is i, set left side position as i+1, and set the right end as the end of the list N-1.
If the sum is greater than 0, decrease the right end by 1.
else if, the sum is less than 0, increase the left end by 1,
else, check the uniqueness of the new entry and if it is unique add it to answer list. Continue search for more entry with leftEnd++, rightEnd--.
Java Code:
public ArrayList<ArrayList<Integer>> threeSum(ArrayList<Integer> A) {
ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
Collections.sort(A); // takes O(nlogn)
if (A.size() < 3) return ans;
ArrayList<Integer> triplet = new ArrayList<>();
for(int i = 0; i < A.size()-3; i++){ // takes O(n^2)
if (i > 0 && A.get(i) == A.get(i-1)) continue; // to maintain unique entries
int r = A.size()-1;
int l = i+1;
while (l < r){
int s = sumOfThree(A, i, l, r);
if (s == 0){
if (ans.size() == 0 || !bTripletExists(A, i, l, r, triplet)){
triplet = getNewTriplet(A, i, l, r); // to be matched against next triplet
ans.add(triplet);
}
l++;
r--;
}else if (s > 0){
r--;
}else {
l++;
}
}
}
return ans;
}
public int sumOfThree(ArrayList<Integer> A, int i, int j, int k){
return A.get(i)+A.get(j)+A.get(k);
}
public ArrayList<Integer> getNewTriplet(ArrayList<Integer> A, int i, int j, int k){
ArrayList<Integer> newTriplet = new ArrayList<>();
newTriplet.add(A.get(i));
newTriplet.add(A.get(j));
newTriplet.add(A.get(k));
return newTriplet;
}
public boolean bTripletExists(ArrayList<Integer> A, int i, int j, int k, ArrayList<Integer> triplet){
if (A.get(i).equals(triplet.get(0)) &&
A.get(j).equals(triplet.get(1)) &&
A.get(k).equals(triplet.get(2)))
return true;
return false;
}
Most of the answers given above are great but fails some edge cases on leetcode.
I added a few more checks to pass all the test cases
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# if the list has less than 3 elements
if len(nums)<3:
return []
# if nums is just zeroes return just one zeroes pair
elif sum([i**2 for i in nums]) == 0:
return [[0,0,0]]
nums.sort()
result = []
for i in range(len(nums)):
#duplicate skip it
if i > 0 and nums[i]== nums[i-1]:
continue
# left pointer starts next to current i item
l = i+1
r = len(nums)-1
while l< r:
summ = nums[l] + nums[r]
# if we find 2 numbers that sums up to -item
if summ == -nums[i]:
result.append([nums[i],nums[l],nums[r]])
l +=1
# duplicate skip it
while l<r and nums[l] == nums[l-1]:
l +=1
# if the sum is smaller than 0 we move left pointer forward
elif summ + nums[i] < 0:
l +=1
# if the sum is bigger than 0 move the right pointer backward
else:
r -=1
return result

Find next highest lexicgraphic permutation of a string [duplicate]

This question already has answers here:
How to get the next lexicographically bigger string in a sorted list by using itertools module?
(5 answers)
Closed 6 years ago.
given a string W, what i want to achieve its next string lexicographically greater.
eg 1:
givenstring = "hegf"
nexthighest = "hefg"
what i have tried till now is here,
from itertools import permutations
q = int(input())
for i in range(q):
s = input()
if s == s[::-1]:
print("no answer")
else:
x = ["".join(p) for p in list(permutations(s))]
x.sort()
index = x.index(s)
print(x[index+1])
since this is not the efficient way to solve this. can u please suggest me better way to solve this problem
here is another way to solve this problem
def NextHighestWord(string):
S = [ord(i) for i in string]
#find non-incresing suffix from last
i = len(S) - 1
while i > 0 and S[i-1] >= S[i]:
i = i - 1
if i <= 0:
return False
#next element to highest is pivot
j = len(S) - 1
while S[j] <= S[i -1]:
j = j - 1
S[i-1],S[j] = S[j],S[i-1]
#reverse the suffix
S[i:] = S[len(S) - 1 : i-1 : -1]
ans = [chr(i) for i in S]
ans = "".join(ans)
print(ans)
return True
test = int(input())
for i in range(test):
s = input()
val = NextHighestWord(s)
if val:
continue
else:
print("no answer")
One classic algorithm to generate next permutation is:
Step 1: Find the largest index k, such that A[k] < A[k + 1].
If not exist, this is the last permutation. (in this problem just reverse the vector and return.)
Step 2: Find the largest index l, such that A[l] > A[k] and l > k.
Step 3: Swap A[k] and A[l].
Step 4: Reverse A[k + 1] to the end.
Here is my C++ snippet of above algorithm. Though its not python, the syntax is simple and pseudo-code alike, hope you will get the idea.
void nextPermutation(vector<int> &num) {
int k = -1;
int l;
//step1
for (int i = num.size() - 1; i > 0; --i) {
if (num[i - 1] < num[i]) {
k = i - 1;
break;
}
}
if (k == -1) {
reverse(num.begin(), num.end());
return;
}
//step2
for (int i = num.size() - 1; i > k; --i) {
if (num[i] > num[k]) {
l = i;
break;
}
}
//step3
swap(num[l], num[k]);
//step4
reverse(num.begin() + k + 1, num.end());
}

Categories