smallest window contains all the elements in an array - python

I need to write a function to find the smallest window that contains all the elements in an array. Below is what I have tried:
def function(item):
x = len(set(item))
i = 0
j = len(item) - 1
result = len(item)
while i <= j:
if len(set(item[i + 1: j + 1])) == x:
result = min(result, len(item[i + 1: j + 1]))
i += 1
elif len(set(item[i:j])) == x:
result = min(result, len(item[i:j]))
j -= 1
else:
return result
return result
print(function([8,8,8,8,1,2,5,7,8,8,8,8]))
The time complexity is in O(N^2), Can someone help me to improve it to O(N) or better? Thanks.

You can use the idea from How to find smallest substring which contains all characters from a given string? for this specific case and get a O(N) solution.
Keep a counter for how many copies of each unique number is included in the window and move the end of the window to the right until all unique numbers are included at least once. Then move the start of the window until one unique number disappears. Then repeat:
from collections import Counter
def smallest_window(items):
element_counts = Counter()
n_unique = len(set(items))
characters_included = 0
start_enumerator = enumerate(items)
min_window = len(items)
for end, element in enumerate(items):
element_counts[element] += 1
if element_counts[element] == 1:
characters_included += 1
while characters_included == n_unique:
start, removed_element = next(start_enumerator)
min_window = min(end-start+1, min_window)
element_counts[removed_element] -= 1
if element_counts[removed_element] == 0:
characters_included -= 1
return min_window
>>> smallest_window([8,8,8,8,1,2,5,7,8,8,8,8])
5

This problem can be solved as below.
def lengthOfLongestSublist(s):
result = 0
#set a dictionary to store item in s as the key and index as value
d={}
i=0
j=0
while (j < len(s)):
#if find the s[j] value is already exist in the dictionary,
#move the window start point from i to i+1
if (s[j] in d):
i = max(d[s[j]] + 1,i)
#each time loop, compare the current length of s to the previouse one
result = max(result,j-i+1)
#store s[j] as key and the index of s[j] as value
d[s[j]] = j
j = j + 1
return result
lengthOfLongestSubstring([8,8,8,8,8,5,6,7,8,8,8,8,])
Output: 4
Set a dictionary to store the value of input list as key and index
of the list as the value. dic[l[j]]=j
In the loop, find if the current value exists in the dictionary. If
exist, move the start point from i to i + 1.
Update result.
The complexity is O(n).

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

Trouble trying to find length of longest substring

I wrote the following code. It should return to me the length of the longest subscript in a string without a repeat in letters.
def lengthOfLongestSubstring(s):
lst = []
y = 0
final = 0
count = len(s)
while len(s) > 0:
s = s[y:]
for i in range(len(s)):
if s[i] in lst:
y += 1
count = len(lst)
lst =[]
break
else:
lst.append(s[i])
if count > final:
final=count
return(final)
when entering the string "tmmzuxt" i expect to get an output of 5 (length of "mzuxt") but instead get 4. I have debugged to figure out the problem seems to be that my function skips over the second 'm' when indexing but I can't figure out why. Any suggestions?
Realized I somehow missed a line. Hope this makes more sense.
Your issue here is that you are modifying s while you are running your code.
Consider that in the first iteration, you are getting s = s[0:], so s will now be 'tmmzuxt'. In your next iteration, you are getting s = s[1:], from the modified s. This is still not a problem, because you just get 'mmzuxt'. However, in your third iteration, you are getting s = s[2:], which is now 'zuxt'.
So you need a different variable than s to hold the substring of s that you are actually testing.
here, in your code(line 7) you are updating your string value inside function, everytime your for loop iterates.
for e.g., after every break inside for loop. you string(which is "tmmzuxt") is becoming short and short.
i created a new variable which contains your original string.
def lengthOfLongestSubstring(s):
lst = []
y = 0
final = 0
count = len(s)
main_string = s;#change done here
while len(s) > 0:
s = main_string[y:] #change done here
for i in range(len(s)):
if s[i] in lst:
y += 1
count = len(lst)
lst =[]
break
else:
lst.append(s[i])
if count > final:
final =count
print(final)
return(final)
lengthOfLongestSubstring("tmmzuxt")
The main problem with your code is that you incremented y, even though it should only ever remove the first character. There is no need for a variable y. Try this:
def lengthOfLongestSubstring(s):
final = 0
while len(s) > 0:
count = len(s)
lst = []
for i in range(len(s)):
if s[i] in lst:
count = i - 1
break
lst.append(s[i])
if count > final:
final = count
s = s[1:]
return final
print(lengthOfLongestSubstring("tmmzuxt"))
Here is an edited code. removing #lst =[] and #break lines.
[Code]
def lengthOfLongestSubstring(s):
lst = []
y = 0
final = 0
count = len(s)
while len(s) > 0:
s = s[y:]
for i in range(len(s)):
if s[i] in lst:
y += 1
count = len(lst)
#lst =[]
#break
else:
lst.append(s[i])
if count > final:
final=count
return(final)
s="tmmzuxt"
print(lengthOfLongestSubstring(s))
[Output]
5
I'm not sure if I understand your code, or if the while loop is needed here, actually. Try this instead:
def lengthOfLongestSubstring(s):
max_length = 0
length = 0
previous = ''
for thisCharacter in s:
if thisCharacter != previous:
length += 1
else:
max_length = max(length, max_length)
length = 1
return max_length

How to count the number of unique numbers in sorted array using Binary Search?

I am trying to count the number of unique numbers in a sorted array using binary search. I need to get the edge of the change from one number to the next to count. I was thinking of doing this without using recursion. Is there an iterative approach?
def unique(x):
start = 0
end = len(x)-1
count =0
# This is the current number we are looking for
item = x[start]
while start <= end:
middle = (start + end)//2
if item == x[middle]:
start = middle+1
elif item < x[middle]:
end = middle -1
#when item item greater, change to next number
count+=1
# if the number
return count
unique([1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5,5,5])
Thank you.
Edit: Even if the runtime benefit is negligent from o(n), what is my binary search missing? It's confusing when not looking for an actual item. How can I fix this?
Working code exploiting binary search (returns 3 for given example).
As discussed in comments, complexity is about O(k*log(n)) where k is number of unique items, so this approach works well when k is small compared with n, and might become worse than linear scan in case of k ~ n
def countuniquebs(A):
n = len(A)
t = A[0]
l = 1
count = 0
while l < n - 1:
r = n - 1
while l < r:
m = (r + l) // 2
if A[m] > t:
r = m
else:
l = m + 1
count += 1
if l < n:
t = A[l]
return count
print(countuniquebs([1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5,5,5]))
I wouldn't quite call it "using a binary search", but this binary divide-and-conquer algorithm works in O(k*log(n)/log(k)) time, which is better than a repeated binary search, and never worse than a linear scan:
def countUniques(A, start, end):
len = end-start
if len < 1:
return 0
if A[start] == A[end-1]:
return 1
if len < 3:
return 2
mid = start + len//2
return countUniques(A, start, mid+1) + countUniques(A, mid, end) - 1
A = [1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,4,5,5,5,5,5,5,5,5,5,5]
print(countUniques(A,0,len(A)))

Use python to solve a math problem: sum up to a value as close as possible

I have a list of numbers. Now if I set a fixed value V, is it possible for python to divide the list into several groups such that each group's sum is not smaller than V (get those groups as many as possible)?
Ex: if the list is [1,2,3,4,5] and the V is 6, then the result should be [[1,5],[2,3,4]]. Dividing the group means you cannot use the same original item more than once.
There's no limitation on how many items each sublist can contain and also the numbers are not in order (can be some random numbers). Could someone help me out? So far my solution is sum up all the combinations and compare the sums. But I'm pretty sure there should be a more effective solution. Thanks!
My solution: I kinda use this first and do the rest job by my mind, so it doesn't worth a further development.
import itertools
import math
stuff = list(range(10))
v = 6
for L in range(0, len(stuff)+1):
for subset in itertools.combinations(stuff, L):
if math.fsum(subset) > v:
print(subset,math.fsum(subset))
My solution has O(n^2) time complexity. You can sort list in ascending order. Then iterate over list from the end. As You want max number of subsets, then each value greater then V added to array. In other case collect values from right and left corners while achieve sum of subset equals to V:
def get_value_in_dict(d):
return d.get(list(d)[0])
# Implementation for a list of dictionaries like [{'apple':1},{'pear':22},{'hat':23},{'glass':44}]
def sum_up_to_value(stuff, val):
new_stuff = []
sorted_stuff = list(sorted(stuff, key=lambda el: get_value_in_dict(el)))
n = len(stuff)
pointer_r = n - 1
pointer_l = 0
queue = list()
while pointer_r >= pointer_l:
if get_value_in_dict(sorted_stuff[pointer_r]) >= val:
new_stuff.append([sorted_stuff[pointer_r]])
else:
subsum = get_value_in_dict(sorted_stuff[pointer_r])
substuff = []
while pointer_l < pointer_r and subsum < val:
# get from queue
while len(queue) and subsum < val:
temp = queue.pop(0)
subsum += get_value_in_dict(temp)
substuff.append(temp)
# get from input
else:
if subsum < val:
subsum += get_value_in_dict(sorted_stuff[pointer_l])
substuff.append(sorted_stuff[pointer_l])
pointer_l += 1
substuff.append(sorted_stuff[pointer_r])
# returns back smallest elements
while subsum - get_value_in_dict(substuff[0]) >= val:
temp = substuff.pop(0)
queue.append(temp)
subsum -= get_value_in_dict(substuff[0])
if subsum < val:
# add substuff to last element of new_stuff
temp = new_stuff.pop()
new_stuff.append(temp + substuff)
else:
new_stuff.append(substuff)
pointer_r -= 1
return new_stuff # list(map(lambda el: sorted(el, key=lambda el_d: get_value_in_dict(el_d)), new_stuff)) for sorted by value elements in resulting list
If you sort your list, this should get you the desired result, although it's not very beautiful as I have to admit:
stuff = range(10)
l = len(stuff)
v = 6
new_stuff = []
i = 0
active = True
while active:
sub_stuff = []
subsum = 0
while subsum<v:
if i>(l-1):
active = False
break
el = stuff[i]
sub_stuff.append(el)
subsum += el
i += 1
else:
new_stuff.append(sub_stuff)
It just goes through your list and sums element until their sum is 6 or higher and then appends a list of those elements to new_stuff and goes on to find the next list.
Try a test list like:
import numpy as np
stuff = sorted(np.random.randint(0,10,100))

Alternate letters in a string - code not working

I am trying to make a string alternate between upper and lower case letters. My current code is this:
def skyline (str1):
result = ''
index = 0
for i in str1:
result += str1[index].upper() + str1[index + 1].lower()
index += 2
return result
When I run the above code I get an error saying String index out of range. How can I fix this?
One way using below with join + enumerate:
s = 'asdfghjkl'
''.join(v.upper() if i%2==0 else v.lower() for i, v in enumerate(s))
#'AsDfGhJkL'
This is the way I would rewrite your logic:
from itertools import islice, zip_longest
def skyline(str1):
result = ''
index = 0
for i, j in zip_longest(str1[::2], islice(str1, 1, None, 2), fillvalue=''):
result += i.upper() + j.lower()
return result
res = skyline('hello')
'HeLlO'
Explanation
Use itertools.zip_longest to iterate chunks of your string.
Use itertools.islice to extract every second character without building a separate string.
Now just iterate through your zipped iterable and append as before.
Try for i in range(len(str1)): and substitute index for i in the code. After, you could do
if i % 2 == 0: result += str1[i].upper()
else: result += str1[i].lower()
For every character in your input string, you are incrementing the index by 2. That's why you are going out of bounds.
Try using length of string for that purpose.
you do not check if your index is still in the size of your string.
It would be necessary to add a condition which verifies if the value of i is always smaller than the string and that i% 2 == 0 and that i == 0 to put the 1st character in Upper
with i% 2 == 0 we will apply the upper one letter on two
for i, __ in enumerate(str1):
if i+1 < len(str1) and i % 2 == 0 or i == 0:
result += str1[i].upper() + str1[i + 1].lower()
I tried to modify as minimal as possible in your code, so that you could understand properly. I just added a for loop with step 2 so that you wouldn't end up with index out of range. And for the final character in case of odd length string, I handled separately.
def skyline (str1):
result = ''
length = len(str1)
for index in range(0, length - 1, 2):
result += str1[index].upper() + str1[index + 1].lower()
if length % 2 == 1:
result += str1[length - 1].upper()
return result
You can use the following code:
def myfunc(str1):
result=''
for i in range(0,len(str1)):
if i % 2 == 0:
result += str1[i].upper()
else:
result += str1[i].lower()
return result
in your code you are get 2 word by one time so you should divide your loop by 2 because your loop work by depending your input string so make an variable like peak and equal it to len(your input input) then peak = int(peak/2) it will solve your pr
def func(name):
counter1 = 0
counter2 = 1
string = ''
peak = len(name)
peak = int(peak/2)
for letter in range(1,peak+1):
string += name[counter1].lower() + name[counter2].upper()
counter1 +=2
counter2 +=2
return string

Categories