To Find Vowel-Substring From a String - python

I have a string of lowercase English letters and an integer of the substring length. I have to find the substring of that length that contains the most vowels.
Example:
s = 'azerdii'
k = 5
The possible 5 character substrings are:
'azerd' No. of vowels = 2
'zerdi' No. of vowels = 2
'erdii' No. of vowels = 3
So the answer should be 'erdii'
Here is my code:
def findSubstring(s, k):
i = 0
lst = []
count = 0
tempL = []
while(i != len(s)):
a = i+k
temp = s[i:a]
lst.append(temp)
if a != len(s):
i+=1
else:
break
for word in lst:
for alphabet in word:
if alphabet in 'aeiou':
count += 1
tempL.append(count)
print(lst)
print(tempL)
return
s = input()
k = int(input().strip())
print(findSubstring(s, k))
I'm getting
['azerd', 'zerdi', 'erdii']
[2, 4, 7]
But the count should be
[2, 2, 3]
Please forgive any stupid errors I may have. I will certainly appreciate any help.

You need to reset the count=0 at line number 17.
Try this code
def findSubstring(s, k):
i = 0
lst = []
count = 0
tempL = []
while(i != len(s)):
a = i+k
temp = s[i:a]
lst.append(temp)
if a != len(s):
i+=1
else:
break
for word in lst:
count = 0
for alphabet in word:
if alphabet in 'aeiou':
count += 1
tempL.append(count)
print(lst)
print(tempL)
return
s = 'azerdii'
k = 5
print(findSubstring(s, k))

You could use a sliding window approach, for an optimal time complexity single-pass solution:
def find_substring_length_k_most_vowels(s: str, k: int) -> str:
'''Returns first substring of length k that has the max number of vowels.'''
vowels = set('aeiou')
max_vowel_count = curr_vowel_count = 0
max_window_start, max_window_end = 0, -1
window_start = 0
for window_end, ch in enumerate(s):
if ch in vowels:
curr_vowel_count += 1
if window_end - window_start + 1 == k:
if curr_vowel_count > max_vowel_count:
max_vowel_count = curr_vowel_count
max_window_start, max_window_end = window_start, window_end
curr_vowel_count -= 1 if s[window_start] in vowels else 0
window_start += 1
return s[max_window_start:max_window_end + 1]
def main() -> None:
s = 'azerdii'
k = 5
print(find_substring_length_k_most_vowels(s, k))
if __name__ == '__main__':
main()
Output:
erdii

Try the following:
def find_substr(s, k):
substrings = [s[i:i+k] for i in range(len(s) - k + 1)] # list of substrings
# vowels = [sum(c in 'aeiou' for c in s) for s in substrings]
# print(vowels) # [2, 2, 3]
return max(substrings, key=lambda s: sum(c in 'aeiou' for c in s))
print(find_substr('azerdii', 5)) # 'erdii'
If you un-comment the lines that are commented out, you will see the number of vowels is correctly computed as [2, 2, 3].
Here, sum(c in 'aeiou' for c in s) gets the number of vowels in a string s, which is equivalent to
count = 0
for alphabet in word:
if alphabet in 'aeiou':
count += 1
which in turn is the same as your code, except the line count = 0. After processing each word, you need to reset count. So try adding count = 0 in your code.

Brute Force Solution
Intuition :
Initialize a set and push all the elements of the sub string in that set(knowing that the element is a vowel). When its sizes == 5(as there are 5 vowels in English alphabet) then increase the counter by 1
Approach :
Make a function isVowel that return (1/true) when the passed character is a vowel otherwise (0/false).
Initialize 2 for loop
a. Outer loop running from 0 to word.size()(till the end of the string).
b. Inner loop running from i to word.size()(till the end of the string).
Now initialize a SET inside the outer for loop (as we don’t want that the values of previous iteration to persist in it).
In the inner for loop initialize a condition that if the J th Character value is not a vowel then break the loop.
And if it is a vowel then insert it in the initailzed SET
Now again initialize a condition that if the size of SET is == 5 then increase the counter by 1.
At last return the counter.
Code:
// Function that checks that the given character is a vowel or not
int isVowel(char c)
{
if ((c == 'a') || (c == 'e') || (c == 'i') || (c == 'o') || (c == 'u'))
{
return 1;
}
return 0;
}
int countVowelSubstrings(string word)
{
int counter = 0; // initalize the counter with 0
for (int i = 0; i < word.size(); i++)
{
set<char> temp;// set initalized outside the for loop so as to avoid
// the persitance of the pervious iteration values in
//the set
for (int j = i; j < word.size(); j++)
{
// if the jth charcter is not a vowel then break the loop
if (isVowel(word[j]) == 0)
{
break;
}
// else insert the char value in the SET
temp.insert(word[j]);
// if size == 5 (as there are 5 vowels in English alphabet) then
// increase the counter by 1
if (temp.size() == 5)
{
counter++;
}
}
}
return counter;
}
// Code By Ambuj Kumar

An almost one-liner functional approach:
from functools import reduce
def getVowelsLength(word, k):
chunks = [word[i:i+k] for i in range(len(word)-(k-1))]
return reduce(lambda x, y: x if x[1] > y[1] else y, list(zip(chunks, [sum(1 for l in w if l in 'aeiou') for w in chunks])))
which will give the correct answer: ('erdii', 3)

Related

Code for consecutive strings works but can't pass random tests

In this problem, I'm given an array(list) strarr of strings and an integer k. My task is to return the first longest string consisting of k consecutive strings taken in the array. My code passed all the sample tests from CodeWars but can't seem to pass the random tests.
Here's the link to the problem.
I did it in two days. I found the max consecutively combined string first. Here's the code for that.
strarr = []
def longest_consec(strarr, k):
strarr.append('')
length = len(strarr)
cons_list = []
end = k
start = 0
freq = -length/2
final_string = []
largest = max(strarr, key=len, default='')
if k == 1:
return largest
elif 1 < k < length:
while(freq <= 1):
cons_list.append(strarr[start:end])
start += k-1
end += k-1
freq += 1
for index in cons_list:
final_string.append(''.join(index))
return max(final_string, key=len, default='')
else:
return ""
Since that didn't pass all the random tests, I compared the combined k strings on both sides of the single largest string. But, this way, the code doesn't account for the case when the single largest string is in the middle. Please help.
strarr = []
def longest_consec(strarr, k):
strarr.append('')
length = len(strarr)
largest = max(strarr, key=len, default='')
pos = int(strarr.index(largest))
if k == 1:
return largest
elif 1 < k < length:
prev_string = ''.join(strarr[pos+1-k:pos+1])
next_string = ''.join(strarr[pos:pos+k])
if len(prev_string) >= len(next_string):
res = prev_string
else:
res = next_string
return res
else:
return ""
print(longest_consec(["zone", "abigail", "theta", "form", "libe"], 2))
Let's start from the first statement of your function:
if k == 1:
while(p <= 1):
b.append(strarr[j:i])
j += 1
i += 1
p += 1
for w in b:
q.append(''.join(w))
return max(q, key=len)
Here q is finally equal strarr so you can shorten this code to:
if k == 1:
return max(strarr, key=len)
I see that second statement's condition checks if k value is between 1 and length of string array inclusive:
elif k > 1 and k <= 2*a:
...
If you want no errors remove equality symbol, last element of every array has index lesser than its length (equal exactly length of it minus 1).
Ceiling and division is not necessary in a definition, so you can shorten this:
a = ceil(len(strarr)/2)
into this:
a = len(strarr)
then your elif statement may look like below:
elif 1 < k < a: # Same as (k > 1 and k < a)
...
again, I see you want to concatenate (add) the longest string to k next strings using this code:
while(p <= 1):
b.append(strarr[j:i])
j += k-1
i += k-1
p += 1
for w in b:
q.append(''.join(w))
return max(q, key=len)
the more clearer way of doing this:
longest = max(strarr, key=len) # Longest string in array.
index = 0 # Index of the current item.
for string in strarr:
# If current string is equal the longest one ...
if string == longest:
# Join 'k' strings from current index (longest string index).
return ''.join(strarr[index:index + k])
index += 1 # Increase current index.
And the last statement which is:
elif k > 2*a or k<1:
return ""
if all previous statements failed then value is invalid so you can instead write:
return "" # Same as with else.
Now everything should work. I advice you learning the basics (especially lists, strings and slices), and please name your variables wisely so they are more readable.
You can try this as well
this has passed all the test cases on the platform you suggested.
def longest_consec(strarr, k):
i = 0
max_ = ""
res = ""
if (k<=0) or (k>len(strarr)):
return ""
while i<=(len(strarr)-k):
start = "".join(strarr[i:i+k])
max_ = max(max_, start, key=len)
if max_==start:
res=strarr[i:i+k]
i+=1
return max_
#output: ["zone", "abigail", "theta", "form", "libe", "zas", "theta", "abigail"], 2 -> abigailtheta
#output: ["zones", "abigail", "theta", "form", "libe", "zas", "theta", "abigail"],2 -> zonesabigail

count characters occurences in string

I want to find out how often does "reindeer" (in any order) come in a random string and what is the left over string after "reindeer" is removed. I need to preserve order of the left over string
So for example
"erindAeer" -> A (reindeer comes 1 time)
"ierndeBeCrerindAeer" -> ( 2 reindeers, left over is BCA)
I thought of sorting and removing "reindeer", but i need to preserve the order . What's a good way to do this?
We can replace those letters after knowing how many times they repeat, and Counter is convenient for counting elements.
from collections import Counter
def leftover(letter_set, string):
lcount, scount = Counter(letter_set), Counter(string)
repeat = min(scount[l] // lcount[l] for l in lcount)
for l in lcount:
string = string.replace(l, "", lcount[l] * repeat)
return f"{repeat} {letter_set}, left over is {string}"
print(leftover("reindeer", "ierndeBeCrerindAeer"))
print(leftover("reindeer", "ierndeBeCrerindAeere"))
print(leftover("reindeer", "ierndeBeCrerindAee"))
Output:
2 reindeer, left over is BCA
2 reindeer, left over is BCAe
1 reindeer, left over is BCerindAee
Here is a rather simple approach using collections.Counter:
from collections import Counter
def purge(pattern, string):
scount, pcount = Counter(string), Counter(pattern)
cnt = min(scount[x] // pcount[x] for x in pcount)
scount.subtract(pattern * cnt)
return cnt, "".join(scount.subtract(c) or c for c in string if scount[c])
>>> purge("reindeer", "ierndeBeCrerindAeer")
(2, 'BCA')
Here is the code in Python:
def find_reindeers(s):
rmap = {}
for x in "reindeer":
if x not in rmap:
rmap[x] = 0
rmap[x] += 1
hmap = {key: 0 for key in "reindeer"}
for x in s:
if x in "reindeer":
hmap[x] += 1
total_occ = min([hmap[x]//rmap[x] for x in "reindeer"])
left_over = ""
print(hmap, rmap)
for x in s:
if (x in "reindeer" and hmap[x] > total_occ * rmap[x]) or (x not in "reindeer"):
left_over += x
return total_occ, left_over
print(find_reindeers("ierndeBeCrerindAeer"))
Output for ierndeBeCrerindAeer:
(2, "BCA")
You can do it by using count and replace string function:
import queue
word = "reindeer"
given_string = "ierndeBeCrerindAeer"
new_string = ""
counter = 0
tmp = ""
letters = queue.Queue()
for i in given_string:
if not i in word:
new_string += i
else:
letters.put(i)
x = 0
while x < len(word):
while not letters.empty():
j = letters.get()
if j == word[x]:
tmp += j
# print(tmp)
break
else:
letters.put(j)
x = x +1
if tmp == word:
counter += 1
tmp = ""
x = 0
print(f"The word {word} occurs {counter} times in the string {given_string}.")
print("The left over word is",new_string)
Output will be:
The word reindeer occurs 2 times in the string ierndeBeCrerindAeer.
The left over word is BCA
It's easy to use queue here so that we don't repeat the elements that are already present or found.
Hope this answers your question, Thank you!

Creating a function that translates number to letter

I have written this function which is supposed to go through a user-provided string like 1-3-5, and output a corresponding series of letters, where A is assigned to 1, B is assigned to 2, C is assigned to 3, etc. So in the case of 1-3-5 the output would be ACE. For 2-3-4, it should print BCD. For ?-3-4 or --3-4 it should still print BCD. Here is the code I have written so far:
def number_to_letter(encoded):
result = ""
start = 0
for char in range(len(encoded)):
if encoded[char] == '-':
i = encoded.index("-")
sub_str = encoded[start:i]
if not sub_str.isdigit():
result += ""
else:
letter = chr(64 + int(sub_str))
if 0 < int(sub_str) < 27:
result += letter
else:
result += ""
start += len(sub_str) + 1
return result
print(num_to_let('4-3-25'))
My output is D, when it should be DCY. I am trying to do this without using any lists or using the split function, just by finding the - character in the sub-string and converting the numbers before it into a letter. What can I do?
You can try doing something like this:
def number_to_letter(encoded):
result = ""
buffer = ""
for ch in encoded:
if ch == '-':
if buffer and 0 < int(buffer) < 27:
result += chr(64 + int(buffer))
buffer = ""
elif ch.isdigit():
buffer += ch
else:
if buffer and 0 < int(buffer) < 27:
result += chr(64 + int(buffer))
return result
print(number_to_letter('1-3-5'))
output:
ACE
Explanation:
we loop for each character and add it to some buffer. when we encounter - (delimiter) we try to parse the buffer and reset it. And we do the same parsing at the end one more time and return the result.
The way the validation works is that, whenever we populate the buffer we check for number validity (using .isdigit()) and when we parse the buffer we check for the range constraints.
import string
alphabet = list(string.ascii_lowercase)
combination = "1-2-3"
def seperate(s, sep='-'):
return [s[:s.index(sep)]] + seperate(s[s.index(sep)+1:]) if sep in s else [s]
combination = seperate(combination)
print("".join([alphabet[int(i)-1] for i in combination]))
the approach of this code is to find the first '-' and then store where it is so next time we can look for the first '-' after the last one
when the comments in my code talk about a cycle means going through the loop (While looping:) once
def number_to_letter(encoded):
letterString = ""
startSubStr = 0
endSubStr = 0
looping = True
while looping:
if endSubStr > (len(encoded)-4):# if we're at the last number we don't look for '-'. we go to the end of the str and end the loop
endSubStr = len(encoded)
looping = False
else:
endSubStr = encoded.index('-', startSubStr) #find the first '-' after the '-' found in the last cycle
number = int(encoded[startSubStr:endSubStr]) #get the number between the '-' found in the last cycle through this loop and the '-' found in this one
if number < 27:
letter = chr(64 + int(number))
letterString += letter
startSubStr = endSubStr + 1 #set the start of the substring to the end so the index function doesn't find the '-' found in this cycle again
return letterString
print(number_to_letter("23-1-1-2")) #>>> WAAB
result:
WAAB
I see you don't want to use split, how about filter? ;)
import itertools
s = '1-2-3'
values = [''.join(e) for e in filter(
lambda l: l != ['-'],
[list(g) for k, g in itertools.groupby(
[*s], lambda s: s.isnumeric()
)
]
)
]
That will essentially do what .split('-') does on s. Also list(s) will behave the same as [*s] if you wanna use that instead.
Now you can just use ord and chr to construct the string you require-
start_pivot = ord('A') - 1
res = ''.join([chr(int(i) + start_pivot) for i in values])
Output
>>> s = '2-3-4'
>>> values = [''.join(e) for e in filter(
...: lambda l: l != ['-'],
...: [list(g) for k, g in itertools.groupby(
...: [*s], lambda s: s.isnumeric()
...: )
...: ]
...: )
...: ]
>>> start_pivot = ord('A') - 1
>>> res = ''.join([chr(int(i) + start_pivot) for i in values])
>>> res
'BCD'
No lists, no dicts. What about RegExp?
import re
def get_letter(n):
if int(n) in range(1,27): return chr(int(n)+64)
def number_to_letter(s):
return re.sub(r'\d+', lambda x: get_letter(x.group()), s).replace('-','')
print(number_to_letter('1-2-26')) # Output: ABZ
No lists, okay. But what about dicts?
def abc(nums):
d = {'-':'','1':'A','2':'B','3':'C','4':'D','5':'E','6':'F','7':'G','8':'H','9':'I','0':'J'}
res = ''
for n in nums: res += d[n]
return res
print(abc('1-2-3-9-0')) # Output: ABCIJ
Here is a corrected version:
def abc(nums):
d = {'-':'','1':'A','2':'B','3':'C','4':'D','5':'E','6':'F','7':'G','8':'H','9':'I','0':'J'}
res = ''
for n in nums:
if n in d:
res += d[n]
return res
print(abc('?-2-3-9-0')) # Output: BCIJ

How to count the number of dashes between any two alphabetical characters?

If we have a string of alphabetical characters and some dashes, and we want to count the number of dashes between any two alphabetic characters in this string. what is the easiest way to do this?
Example:
Input: a--bc---d-k
output: 2031
This means that there are 2 dashes between a and b, 0 dash between b and c, 3 dashes between c and d and 1 dash between d and k
what is a good way to find this output list in python?
You can use a very simple solution like this:
import re
s = 'a--bc---d-k'
# Create a list of dash strings.
dashes = re.split('[a-z]', s)[1:-1]
# Measure the length of each dash string in the list and join as a string.
results = ''.join([str(len(i)) for i in dashes])
Output:
'2031'
Solution with regex:
import re
x = 'a--bc---d-k'
results = [
len(m) for m in
re.findall('(?<=[a-z])-*(?=[a-z])', x)
]
print(results)
print(''.join(str(r) for r in results))
output:
[2, 0, 3, 1]
2031
Solution with brute force loop logic:
x = 'a--bc---d-k'
count = 0
results = []
for c in x:
if c == '-':
count += 1
else:
results.append(count)
count = 0
results = results[1:] # cut off first length
print(results)
output:
[2, 0, 3, 1]
If you input may also begin with a dash, you could use this:
def count_dashes(string):
all_counts = []
dash_count = 0
for char in string:
if char == "-":
dash_count += 1
else:
all_counts.append(dash_count)
dash_count = 0
return all_counts
But if your input always starts with a letter, you may not like the 0 that's always at the head of the list.
If you need the output as a string of ints, then you could add this:
def count_dashes(string):
all_counts = []
dash_count = 0
for char in string:
if char == "-":
dash_count += 1
else:
all_counts.append(dash_count)
dash_count = 0
return "".join([str(number) for number in all_counts])
Here's a simple loop approach:
myinput = 'a--bc---d-k'
output = []
output_count = -1
for elem in myinput:
if elem == '-':
output[output_count] = output[output_count]+1
else:
output.append(0)
output_count += 1
print(output)

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

Categories