This is a problem from HackerRank. My implementation as shown below passes most of the test but for the tests that it fails, it states that it is taken too long. After looking at other submissions, I found that another user's implementation (credit to saikiran9194) passes all tests almost immediately. I really am having trouble understanding why his solution is the most efficient at scale.
My Implementation:
m, n = map(int, input().strip().split(' '))
magazine = input().strip().split(' ')
ransom = input().strip().split(' ')
yesNo = "Yes"
for i in ransom:
if(ransom.count(i) > magazine.count(i)):
yesNo = "No"
print(yesNo)
More Time Efficient Implementation
def ransom_note(magazine, ransom):
rc = {} # dict of word: count of that word in the note
for word in ransom:
if word not in rc:
rc[word] = 0
rc[word] += 1
for word in magazine:
if word in rc:
rc[word] -= 1
if rc[word] == 0:
del rc[word]
if not rc:
return True
return False
m, n = map(int, input().strip().split(' '))
magazine = input().strip().split(' ')
ransom = input().strip().split(' ')
answer = ransom_note(magazine, ransom)
if(answer):
print("Yes")
else:
print("No")
It's the difference between list.count and dict.__getitem__ (rc[word]). list.count is O(n) whereas dict.__getitem__ is O(1) due to, as you mention, hashing.
Source: https://wiki.python.org/moin/TimeComplexity
list.count has linear complexity, so your code has quadratic complexity overall, doing linear work in each iteration of the loop. By putting the lists in a dict first, it only needs O(1) to get the count for a certain letter.
You can just wrap those lists into collections.Counter (not tested):
m, n = map(int, input().strip().split())
magazine = Counter(input().strip().split())
ransom = Counter(input().strip().split())
yesNo = "Yes"
for i in ransom:
if(ransom[i] > magazine[i]):
yesNo = "No"
print(yesNo)
Or shorter using any
yesno = "No" if any(random[i] > magazine[i] for i in ransom) else "Yes"
Related
I use this code to syllable a list of strings, I need to be able to speed up this code to pass some tests, any ideas to improve this code and make it faster?
def separate(word):
s = ""
L = []
voc = ['a','e','i','o','u','y','j']
for letter in word:
if len(s) < 1:
s += letter
elif s[len(s) - 1] not in voc:
s += letter
elif s.startswith('aeiouyj'):
s +=letter
elif letter in voc:
s += letter
else:
L.append(s)
s = letter
L.append(s)
return L
Did some small misc adjustments.
def separate(word):
s=''
L = []
for letter in word:
if s=='':
s = letter
elif s[-1] not in 'aeiouyj' or letter in 'aeiouyj':
s += letter
else:
L.append(s)
s = letter
L.append(s)
return L
Not sure if s.startswith('aeiouyj') is of any use in original code because it never going to be True.
def separate(word):
s = ""
L = []
voc = ['a','e','i','o','u','y','j']
s=word[0]
for letter in word[1:]:
if s[len(s) - 1] not in voc:
s += letter
elif letter in voc or s.startswith('aeiouyj'):
s +=letter
else:
L.append(s)
s = letter
L.append(s)
return L
After analysing the code, we can see that it is going into first if just in the beginning. So, we can skip this part. One more thing you can do bring the if before last else to the second place. After analysing a Harry Potter paragraph with this program it enters into loops "1,2436,0,98,959" times respectively. The third if maybe eliminated too, since it never entered into this branch in my case.
Instead of using loops you can use recursion which is faster than it. Also Functional programming is always preferred as it is based on input.
For my beginning python class we have to write a code that determines if a number is prime, finds the average word length in a sentence, determines which words in a sentence have prime lengths, and then prints the words that have prime lengths. (we don't need to worry about punctuation for this assignment) So I've written the following code, but when I try to print the words that have prime lengths it's returning list index out of range The code works fine through the first for loop, and returns True for however many prime lengths are entered, but I can't get it to print the actual words. Please help!
from statistics import mean
def is_prime(n):
if n < 2:
return(False)
else:
for i in range(2, n + 1):
if n == 2:
return(True)
break
elif n % i == 0:
return(False)
break
else:
return(True)
break
return
user_input = " "
while user_input != "quit":
user_input = input("Please enter a random sentence (or type 'quit' to exit): ")
word_list = user_input.split()
x = len(word_list)
word_length = []
for i in range(x):
word_length.append(len(word_list[i]))
for i in word_length:
if is_prime(i) == True:
print(word_list[i]) #I think this is where the problem is???
avg = mean(word_length)
print("Average word length: " + str(avg))
print("The following words have prime lengths: ")
while user_input == "quit":
print("Goodbye!")
break
In your second loop value of i will be the length of words and the length of the word_list is different, let's say sentense is : "I am a Student" then list word_length look like [1,2,1,7] and you print word_list[1],word_list[2],word_list[1] and word_list[7], but the length of the word_list is 4 so it will give an IndexError for word_list[7], so the right code is look like as follows:
for i in range(x): #here, i starts from 0 and ends at length of word_list
if is_prime(len(word_list[i])) == True:
print(word_list[i])
The following loop is not required.
for i in range(x):
word_length.append(len(word_list[i]))
Change the second for loop to -
for i in range(len(word_length)):
if is_prime(word_length[i]) == True:
print(word_list[i]) #I think this is where the problem is???
You are using the actual word length to access an indexed list which doesn't work if the number of elements in the word_list isn't as long as the length of a word. Looping over range(len(word_length)) solves the problem
user_input,PRIME,NON_PRIME = '',[],[]
while user_input != "quit":
user_input = input("Please enter a random sentence (or type 'quit' to exit):")
if user_input == "quit":break
else:
for i in user_input.split(" "):#user_input text converted to list with separator = ','
#PRIME CHECK
if len(i) > 1:
for j in range(2,len(i)):
if (len(i)% j) == 0:
NON_PRIME.append([i,len(i)])
break
else:PRIME.append([i,len(i)])
else:NON_PRIME.append([i,len(i)])
print(" PRIME:",PRIME,"\n NON PRIME",NON_PRIME)
OUTPUT
Please enter a random sentence (or type 'quit' to exit):PRIME CHECKER WITH PYTHON
PRIME: [['PRIME', 5], ['CHECKER', 7]]
NON PRIME [['WITH', 4], ['PYTHON', 6]]
Both of these pieces of code do the same thing, which is they check if the words in the list magazine_words are sufficient to make up the message dictated by the words in the list note_words . However the first piece of code takes a lot more time to execute, which doesn't let it run for large inputs. Why is that? Since both solutions use single for-loops, shouldn't they have the same complexity, i.e. take about the same time to run?
First solution:
lengths = input().split ()
m = int(lengths[0])
n = int(lengths [1])
magazine_words = input ().split()
note_words = input ().split()
def checkMagazine(m,n,magazine_words,note_words):
flag = "Yes"
for ind in range (n):
if not (note_words[ind] in magazine_words):
flag = "No"
break
else:
magazine_words.remove(note_words[ind])
return flag
print (checkMagazine(m,n,magazine_words,note_words))
Second solution:
def ransom_note(magazine, ransom):
rc = {} # dict of word: count of that word in the note
for word in ransom:
if word not in rc:
rc[word] = 0
rc[word] += 1
for word in magazine:
if word in rc:
rc[word] -= 1
if rc[word] == 0:
del rc[word]
if not rc:
return True
return False
m, n = map(int, input().strip().split(' '))
magazine = input().strip().split(' ')
ransom = input().strip().split(' ')
answer = ransom_note(magazine, ransom)
if(answer):
print("Yes")
else:
print("No")```
magazine_words.remove(note_words[ind]) is secretly another loop - this has to loop through all of magazine_words until it finds note_words[ind], each time you call it.
I am trying to make a hangman game and I am running into trouble with the display. I have a loop that is supposed to put the correctly guessed letters in the right places, however it only shows the correct location for one letter at a time. I thought it would be helpful to save the result of the previous iteration, and then display that, but I am not sure how to do that.
import random,time
hanglist = []
answerlist = []
file_var = open("wordlist.100000")
for n in file_var:
hanglist.append(file_var.readline())
word = random.choice(hanglist)
print("word is",word)
guesses = 10
while guesses != 0:
print("guess a letter")
answer = input()
answerlist.append(answer)
if answer in word:
m = list(word)
for n in m:
if n == answer:
print(answer, end = '')
else:
print('_', end = '')
else:
print("close, but not exactly")
guesses -= 1
And here are the outputs
word is fabric
guess a letter
f
f______guess a letter
a
_a_____guess a letter
To solve your issue just replace if n==answer to if n in answer. But, from the above code, I can see code can't handle these issues:
If the user guesses the same word again and again
After 4 guesses are done and total word is guessed, then code should break out of the loop, which it is not happening.
While reading line, it need to strip the '\n' otherwise its really hard
My code addresses these issue:
import random,time
hanglist = []
answerlist = []
file_var = open("wordlist.100000")
for n in file_var:
# strips the '/n' at the end
hanglist.append(file_var.readline().rstrip())
word = random.choice(hanglist)
print("word is",word)
guesses = 10
while guesses!=0:
print("guess a letter")
answer = input()
if answer in answerlist:
continue
answerlist.append(answer)
if answer in word:
# to print entire word guessed till now- with current and previous iterations
word_print = ''
for n in word:
# to print all the last state occurences
if n in answerlist:
word_print += n
else:
word_print += '_'
print(word_print,end='')
# word is correctly guessed
if '_' not in word_print:
break
else:
print("close, but not exactly")
guesses = guesses-1
Your issue is with
if n == answer:
print(answer,end = '')
else:
print('_', end = '')
which only compares each letter with the current guess, answer. Instead, if you use
if n in answerlist:
print(n, end = '')
else:
print('_', end = '')
it will show the letter if that letter is in the list of their previous guesses.
Additionally: the previous m= list(word) is not necessary, as for n in word: is valid.
So my code gets two words, and checks if one is the anagram of another one.
However doesn't work if multiple letters are exchanged, although I tried to account for that.
storedword = input("Enter your primary word \t")
global word
word = list(storedword)
word3 = input("Enter anagram word \t")
word3lowercase = word3.lower()
anaw = list(word3lowercase)
counter = int(0)
letterchecker = int(0)
listlength = len(word)
newcounter = int(0)
if len(anaw) != len(word):
print ("not anagram")
if len(anaw) == len(word):
while counter < listlength and newcounter < listlength:
tempcount = 0
if anaw[counter] == word[newcounter]:
temp = word[newcounter]
word[newcounter] = word[tempcount]
word[tempcount]=temp
letterchecker +=1
counter +=1
tempcount +=1
newcounter = int(0)
else:
newcounter +=1
if counter == len(word):
print ("anagram")
else:
print ("not anagram")
I think it's gone somewhere wrong after the if len(anaw) section, for example if the primary word is "hannah", and the secondary word is "hannnn", it thinks it's an anagram.
There is much simpler logic that can be implemented here, even without using sorted and such. Let's assume you have a function anagram:
def anagram(word1, word2):
if len(word1) != len(word2):
return False
def char_count(word):
char_count = {}
for c in word:
char_count[c] = char_count.get(c, 0) + 1
return char_count
cr1 = char_count(word1)
cr2 = char_count(word2)
return cr1 == cr2
You can test this with:
>>> print(anagram("anagram", "aanragm"))
True
>>> print(anagram("anagram", "aangtfragm"))
False
And for future readers, a super simple pythonic solution might be using Counter:
from collections import Counter
>>> Counter(word1) == Counter(word2)
Or using sorted:
>>> sorted(word1) == sorted(word2)
newcounter = int(0)
This is the line that causes the trouble (in the while loop).
Because of it you start checking the word from the beginning again.
I think you want it to be newcounter=letterchecker.
Since already used characters are put to the front of word they are ignored if you start with letterchecker
Tell me if it works
Edit:Checked with example given, seems to work.
Without using sort you could use the following approach. It removes a letter from an array of a characters of the second word. The words are only anagrams if there are no letters left (and the words are the same length to start with and have a length larger than zero):
word1="hannah"
word2="nahpan"
chars1= list(word1)
chars2= list(word2)
if len(chars1)==len(chars2) and len(chars1)>0:
for char in chars1:
if char not in chars2:
break
chars2.remove(char)
if len(chars2)==0:
print "Both words are anagrams"
else:
print "Words are not anagrams"
[EDIT THIS IS JUST FOR PALINDROMES I CANT READ]
Here is something a bit more simple:
storedword = input("Enter your primary word \t")
word3 = input("Enter anagram word \t")
if storedword == word3[::-1]:
print "Is Anagram"
else:
print "Is not anagram"