What is the time and space complexity of the count string function - python

def count(substring, string):
"""
>>> count("bc","abcabcabc")
3
"""
counter = 0
for i in range(len(string) - len(substring) + 1):
counter += string.startswith(substring, i)
return counter
This is a function to count the number of recurrence of a substring in the base string. I think the time complexity is O(n) since I only iterate the string once. I think the space complexity is O(n) as well because I increment the counter in the loop N times. Can someone tell me if I am right or wrong?

The time complexity is O(nm) where n=len(s) and m=len(t) for the reason you provide, but incrementing counter doesn't cause it take up more space, so the space complexity of this function is O(1). No matter how long the input string is, you still only store a single variable, count.
[Edited to correct egregious error pointed out by poster]

Related

Time Complexity for LeetCode 3. Longest Substring Without Repeating Characters

Problem: Given a string s, find the length of the longest substring
without repeating characters.
Example: Input: s = "abcabcbb" Output: 3 Explanation: The answer is
"abc", with the length of 3.
My solution:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
seen = set()
l = r = curr_len = max_len = 0
n = len(s)
while l < n:
if r < n and s[r] not in seen:
seen.add(s[r])
curr_len += 1
max_len = max(curr_len, max_len)
r += 1
else:
l += 1
r = l
curr_len = 0
seen.clear()
return max_len
I know this is not an efficient solution, but I am having trouble figuring out its time complexity.
I visit every character in the string but, for each one of them, the window expands until it finds a repeated char. So every char ends up being visited multiple times, but not sure if enough times to justify an O(n2) time complexity and, obviously, it's way worse than O(n).
You could claim the algorithm to be O(n) if you know the size of the character set your input can be composed of, because the length your window can expand is limited by the number of different characters you could pass over before encountering a duplicate, and this is capped by the size of the character set you're working with, which itself is some constant independent of the length of the string. For example, if you are only working with lower case alphabetic characters, the algorithm is O(26n) = O(n).
To be more exact you could say that it runs in O(n*(min(m,n)) where n is the length of the string and m is the number of characters in the alphabet of the string. The reason for the min is that even if you're somehow working with an alphabet of unlimited unique characters, at worst you're doing a double for loop to the end of the string. That means however that if the number of possible characters you can encounter in the string exceeds the string's length you have a worst case O(n^2) performance (which occurs when every character of the string is unique).

Time Complexity and Space complexity of a program (python)

I have written a python program to Calculate the average word length and return the average word length.
def stringlength(A: List[str]) -> int:
average = sum(len(word) for word in A)/len(A)
return average
test_list = ['gfg', 'is', 'best', 'for', 'geeks']
print(stringlength(test_list))
Answer: 3.4
I am new to calculating time and space complexitities.
I am assuming the time complexity of this python program would be O(N) where N is the length of the list since it uses builtin function like len and for loop
and space complexity would be O(N) since len() in for loop would return a list of lengths.
Can anyone please clarify if my assumption is correct
You are right. Time complexity would be O(n) where n is length of your list.
This is because you are going over every element in your list.

for j in anagram(word[:i] + word[i+1:]): <- how it works?

I built anagram generator. It works, but I don't know for loop for functions works at line 8, why does it works only in
for j in anagram(word[:i] + word[i+1:]):
why not
for j in anagram(word):
Also, I want to know what
for j in anagram(...)
means and doing...
what is j doing in this for loop?
this is my full code
def anagram(word):
n = len(word)
anagrams = []
if n <= 1:
return word
else:
for i in range(n):
for j in anagram(word[:i] + word[i+1:]):
anagrams.append(word[i:i+1] + j)
return anagrams
if __name__ == "__main__":
print(anagram("abc"))
The reason you can't write for i in anagram(word) is that it creates an infinite loop.
So for example if I write the recursive factorial function,
def fact(n):
if n <= 1:
return 1
return n * fact(n - 1)
This works and is not a circular definition because I am giving the computer two separate equations to compute the factorial:
n! = 1
n! = n (n-1)!
and I am telling it when to use each of these: the first one when n is 0 or 1, the second when n is larger than that. The key to its working is that eventually we stop using the second definition, and we instead use the first definition, which is called the “base case.” If I were to instead say another true definition like that n! = n! the computer would follow those instructions but we would never reduce down to the base case and so we would enter an infinite recursive loop. This loop would probably exhaust a resource called the “stack” rapidly, leading to errors about “excessive recursion” or too many “stack frames” or just “stack overflow” (for which this site is named!). And then if you gave it a mathematically invalid expression like n! = n n! it would infinitely loop and also it would be wrong even if it did not infinitely loop.
Factorials and anagrams are closely related, in fact we can say mathematically that
len(anagrams(f)) == fact(len(f))
so solving one means solving the other. In this case we are saying that the anagram of a word which is empty or of length 1 is just [word], the list containing just that word. (Your algorithm messes this case up a little bit, so it's a bug.)
The anagram of any other word must have something to do with anagrams of words of length len(word) - 1. So what we do is we pull each character out of the word and put it at the front of the anagram. So word[:i] + word[i+1:] is the word except it is missing the letter at index i, and word[i:i+1] is the space between these -- in other words it is the letter at index i.
This is NOT an answer but a guide for you to understand the logic by yourself.
Firstly you should understand one thing anagram(word[:i] + word[i+1:]) is not same as anagram(word)
>>> a = 'abcd'
>>> a[:2] + a[(2+1):]
'abd'
You can clearly see the difference.
And for a clearer understanding I would recommend you to print the result of every word in the recursion. put a print(word) statement before the loop starts.

Longest phrase in Tweet - Python timeout

Input - array / list a, constant k
Output - Length of Longest sublist/subarray with sum <=k
E.g. given
I am Bob
i.e. array [1,2,3] and k=3
Sublists possible are [1],[2],[3],[1,2]
Longest sublist here is [1,2]
Length = 2
Issue - TimeOut error in Python on Hackerrank
Time Complexity - 1 for loop - O(n)
Space complexity O(n)
def maxLength(a, k):
lenmax=0
dummy=[]
for i in a:
dummy.append(i)
if sum(dummy)<=k:
lenmax=max(lenmax,len(dummy))
else:
del dummy[0]
return lenmax
Solved it by replacing the time-intensive operation
Time-out occurs when it exceeds the time limit set by HackerRank for every environment "HackerRank TimeOut"
Solution
Replace sum() function by a variable
In worst case, sum(list) would take O(n^2) time if the entire list was to be summed up all the time.
Instead, maintaining a variable would mean O(n) for the entire function as O(1) for updating a variable.
def maxLength(a, k):
lenmax=0
dummy=[]
sumdummy=0
for i in a:
dummy.append(i)
sumdummy+=i
if sumdummy<=k:
lenmax=max(lenmax,len(dummy))
else:
sumdummy-=dummy[0]
del dummy[0]
return lenmax

Removing duplicates from a string in Python without using adittional buffer

I want to resolve this problem in Python:
given a string (without spacing), remove the duplicates without using an adittional buffer.
I have the following code:
def removedup(st):
temp = []
for i in range(len(st)):
if st[i] not in temp:
temp.append(st[i])
return temp
which returns a list without duplicates.
1-This code in O(n^2) right?
2- How I can do the same without using an additional buffer in python?? (I mean not using a list). Maybe I can use a string (not a list) but am not sure if this adds complexity. Also, strings in python are immutable, so I can't do some type of indexing to change something. (Like in C++ or Java).
What is the best way to resolve this in Python? I know that there is some questions that "looks like" duplicates here, but my question is more Python related (solving this without an additional buffer).
Thank you!
1) Yes.
2) Well
return set(st)
..is by far the simplest way to uniquify a string (or any iterable). I don't know if you consider this an "additional buffer" or not. Some extra memory needs to be allocated for another object any way you do it, since strings are immutable as you say.
This of course does not preserve order, and if that's an issue there's always the super-obvious:
from collections import OrderedDict
return ''.join(OrderedDict.fromkeys(st))
0) Apparently you have to use at least one additional buffer since, as you have mentioned, python strings are immutable and you need at least to return result somehow, right? So internaly at least one buffer is already used (even if you name it with the same name).
You can, of course, use string as buffer, they can do string + string or string += string, or even string[:n-1] + string[n:], but because of immutability, internaly it creates new object each time.
You can use some other, mutable, iterable instead of string, so it would work.
1) No, your code is not O(N**2). It's O(N*log(N)) in the worst case scenario (all symbols are unique) and O(N) in best case scenario (all symbols are just one symbol).
2) Assuming that you use list instead of string of string, you could do something like this:
def dup_remove(lst):
i = 0
n = len(lst)
while i < n:
if lst[i] in lst[:i]:
del lst[i]
n -= 1
else:
i += 1
return lst
it's still O(N*Log(N)) in worst case scenario, but it does not use any additional buffers which is what you wanted in the first place. I think that for practical purpose solution with OrderedDict should be more optimal though.
Another way to do it through list slicing loop.
# O(n ^ 2)
for item in input_list[:]:
index = input_list.index(item) + 1
while index < len(input_list):
if input_list[index] == item:
del input_list[index]
index += 1
Since slice creates a copy, if you truly want a solution without any internal buffers, this will do.
# O(n ^ 2)
i = 0
while i < len(input_list):
j = i + 1
while j < len(input_list):
if input_list[j] == input_list[i]:
del input_list[j]
# Don't increment j here since the next item
# after the deleted one will move to index j
else:
j += 1
i += 1
1) I'm not sure.
2) A very efficient way is coded below. Note that I don't use any additional package. I don't even use lists, just a string!
def removeDuplicate (input):
i = 0
while i < len(input)-1:
j = i + 1
while j < len(input):
if input[j] == input[i]:
input_list = input_list[0:j] + input_list[j+1:]
# Don't increment j here since the next item
# after the deleted one will move to index j
else:
j += 1
i += 1
return input

Categories