I have written a small program to check whether two words are anagrams. If two words are anagrams it should return "true" otherwise it should return "False", but I am not getting the correct output. Kindly, tell me what is the mistake in the below program.
def anagram(s1,s2):
for x in s1:
if (x in s2) and (s2.count(x)==s1.count(x)):
pass
return(True)
else:
return(False)
You are iterating over a word and then not doing anything (with pass, the empty statement). Then you return True unconditionally, leaving no chance to return False.
Instead, you can simply sort the two words and then see if they end up the same:
def anagram(s1, s2):
return sorted(s1) == sorted(s2)
Please format this in a more readable way. However, it looks like you are calling return True inside the loop, meaning that if any character occurs the same amount of times in each string, your function will return True
Try this:
def anagram(s1,s2):
for x in s1:
if ( x in s2 ) and (s2.count(x) == s1.count(x) ):
pass
else:
return False
for x in s2:
if ( x in s1 ) and (s1.count(x) == s2.count(x) ):
pass
else:
return False
return True
You may try this:
>>> def anagram(str1,str2):
s1=str1.lower()
s2=str2.lower()
a=len(s1)
b=len(s2)
if a==b:
for c in s1:
if c in s2:
for s in s2:
if s in s1:
return True
else:
return False
else:
return False
You were really close. Your indentation was bad but that is probably due to the text formatting here in SO.
The mistake in your code was that you were returning True too soon. What you have to do is to go through all letters and check for existance and count. Below you can find a corrected and somewhat optimised version of what you were trying to do.
def anagram(s1, s2):
if set(s1) == set(s2) and all(s2.count(x) == s1.count(x) for x in set(s1)):
return True
return False
But then again #Tigerhawk's solution is way better.
Related
I'm dealing with a simple problem:
checking if two strings are anagrams.
I wrote the simple code that can check whether two strings, such as
'abcd' and 'dcba', are anagrams, but I have no idea what to do with more complex ones, like "Astronomer" and "Moon starter."
line1 = input('Enter the first word: ')
line2 = input('Enter the second word: ')
def deleteSpaces(s):
s_new = s.replace(" ","")
return s_new
def anagramSolution2(s1,s2):
alist1 = list(deleteSpaces(s1))
alist2 = list(deleteSpaces(s2))
print(alist1)
print(alist2)
alist1.sort()
alist2.sort()
pos = 0
matches = True
while pos < len(deleteSpaces(s1)) and matches:
if alist1[pos]==alist2[pos]:
pos = pos + 1
else:
matches = False
return matches
Firstly I thought that the problem lies in working with spaces, but then I understood that my algorithm doesn't work if the strings are not the same size.
I have no idea what to do in that case.
Here I found a beautiful solution, but it doesn't work either:
def anagrams(s1,s2):
return [False, True][sum([ord(x) for x in s1]) == sum([ord(x) for x in s2])]
If I run this function and test it on two strings, I'll get such output:
Examples:
First Word: apple
Second Word: pleap
output: True
First Word: Moon starter
Second Word: Astronomer
output: False //however it should should be True because this words are anagrams
Your algorithm is ok. Your problem is that you don't consider upper and lower case letters. Changing the two lines
alist1 = list(deleteSpaces(s1))
alist2 = list(deleteSpaces(s2))
to
alist1 = list(deleteSpaces(s1).lower())
alist2 = list(deleteSpaces(s2).lower())
will solve your issue.
As an alternative you could simply use the following function:
def anagrams(s1, s2):
def sort(s):
return sorted(s.replace(" ", "").lower())
return sort(s1) == sort(s2)
If you want to have a faster solution with complexity of O(n), you should use a Counter instead of sorting the two words:
from collections import Counter
def anagrams(s1, s2):
def get_counter(s):
return Counter(s.replace(" ", "").lower())
return get_counter(s1) == get_counter(s2)
As other's have pointed out, your algorithm is given 'false' results as Moon starter and Astronomer are in fact not anagrams.
You can drastically improve your algorithm by simply using the available object methods. They already provide all the functionality.
def normalize_str(s):
return s.replace(" ","").lower()
def anagramSolution2(s1,s2):
return sorted(normalize_str(s1)) == sorted(normalize_str(s2))
normalize_str is like your deleteSpaces, but it also converts everything to lowercase. This way, Moon and moon will compare equal. You may want to do stricter or looser normalization in the end, it's just an example.
The call to sorted will already provide you with a list, you don't have to do an explicit conversion. Also, list comparison via == will compare the lists element wise (what you are doing with the for loop), but much more efficiently.
Something like this?
$ cat /tmp/tmp1.py
#!/usr/bin/env python
def anagram (first, second):
return sorted(first.lower()) == sorted(second.lower())
if __name__ == "__main__":
for first, second in [("abcd rsh", "abcd x rsh"), ("123 456 789", "918273645 ")]:
print("is anagram('{0}', '{1}')? {2}".format(first, second, anagram(first, second)))
which gives:
$ python3 /tmp/tmp1.py
is anagram('abcd rsh', 'abcd x rsh')? False
is anagram('123 456 789', '918273645 ')? True
a=input("string1:");
b=input("string2:");
def anagram(a,b):
arra=split(a)
arrb=split(b)
arra.sort()
arrb.sort()
if (len(arra)==len(arrb)):
if(arra==arrb):
print ("True")
else:
ana=0;
print ("False");
else:
print ("False");
def split(x):
x=x.replace(' ','').lower()
temp=[]
for i in x:
temp.append(i)
return temp;
anagram(a,b)
Lazy way of doing it but super clean:
def anagram(a,b):
return cleanString(a) == cleanString(b)
def cleanString(string):
return ''.join(sorted(string)).lower().split()
def anagram(str1,str2):
l1=list(str1)
l2=list(str2)
if sorted(l1) == sorted(l2):
print("yess")
else:
print("noo")
str1='listen'
str2='silent'
anagram(str1,str2)
although not the most optimized solution but works fine.
I found this the best way:
from collections import Counter
def is_anagram(string1, string2):
return Counter(string1) == Counter(string2)
I tried this and it worked
def is_anagram(word1,word2):
a = len(word1)
b = len(word2)
if a == b:
for l in word1:
if l in word2:
return True
return False
I would like to create a function with 2 arguments (x,y) ,x and y is a string, and returns true if x is a sub anagram of y. example: "red" is a sub anagram of "reda" but "reda" is not a sub anagram of "red".
So far what I have got:
I have turned x,y into list and then sorted them. That way I can compare the alphabets from each string.
def sub_anagram(str1, str2):
s1 = list(str1)
s2 = list(str2)
s1.sort()
s2.sort()
for letters in s2:
if letters in s1:
return True
else:
return False
What I am confused with:
I want to compare the string y to x and if y contains all the characters from x then it returns true otherwise false
You can use collections.Counter.
from collections import Counter
def subanagram(str1, str2):
str1_counter, str2_counter = Counter(str1), Counter(str2)
return all(str1_counter[char] <= str2_counter[char]
for char in str1_counter)
In the code above, str1_counter is basically a dictionary with the characters appearing in str1 and their frequency as the key, value. Similarly for str2_counter.
Then the code checks that for all characters in str1, that character appears at least as many times in str2 as it does in str1.
Edit: If a subanagram is defined to be strictly smaller than the original, e.g. you want subanagram("red", "red") to be False, then first compare the two counters for equality.
from collections import Counter
def subanagram(str1, str2):
str1_counter, str2_counter = Counter(str1), Counter(str2)
if str1_counter == str2_counter:
return False
return all(str1_counter[char] <= str2_counter[char]
for char in str1_counter)
If I were not using Counter for some reason, it would be something along the lines of:
def subanagram(str1, str2):
if len(str1) == len(str2):
return False #Ensures strict subanagram
s2 = list(str2)
try:
for char in str1:
s2.remove(char)
except ValueError:
return False
return True
But as you can see, it is longer, less declarative and less efficient than using Counter.
I don't think you can just check for each character in x being present in y, as this does not account for a character being repeated in x. In other words, 'reeeeed' is not a sub-anagram of 'reda'.
This is one way to do it:
make a copy of y
for each character in x, if that character is present in the y-copy, remove it from the y-copy. if it isn't present, return false.
if you reach the end of the loop and the y-copy is empty, return false. (x is an anagram, but not a sub-anagram.)
otherwise return true.
I have written the following code to check if some input to the function contains balanced brackets:
def balanced_brackets(text):
brackets = [ ('(',')'), ('[',']'), ('{','}'),('<','>')]
s = 0
e = 1
st = Stack()
for i in text:
for pair in brackets:
if i == pair[s]:
st.push(i)
elif i == pair[e] and not st.isEmpty() and st.pop() != pair[s]:
return False
if st.isEmpty():
return True
else:
return False
This code is working for input such as '()(())()' but it failed when I tried it for 'zn()((b)())q())()l()d(r)'. Can anyone help me identify what the problem is? Thanks.
Your problem is with the and not st.isEmpty()==0. When it gets to the unbalanced ')', all the previous ones have balanced out, so st is empty.
If you have a i == pair[e], and your stack is empty, you want to return False.
You also want to return False if you pop and it isn't pair[e]. But you don't want to pop if the stack is empty.
What you have now, in condition 1, just keeps going. You need to change around the condition there so that it accounts for both, or have two elifs. The former can be achieved with some nesting ands and ors.
By the way; unless you want to do something fancy with it, there's no real need to implement a stack. You can just use a list instead, with l.pop, len(l), and l.append.
This works.It needs a stack module to import. It will keep track of matched pairs.
def multi_bracket_validation(input):
""" test for matching brackets and return bool """
if type(input) is str:
open_b = '({]'
closed_b = ')}]'
compare = Stack()
for brac in input:
if brac in open_b:
compare.push(brac)
elif brac in closed_b:
if compare.top is None:
return False
if closed_b.index(brac) != open_b.index(compare.pop().val):
return False
return compare.top is None
return False
so I'm new to programming (and python) and I have to make this program that returns True if the string has zero or one dot characters ("." characters) and return False if the string contains two or more dots
here is what I currently have, I cannot get it to work for me, please correct me if I am wrong, thanks!
def check_dots(text):
text = []
for char in text:
if '.' < 2 in text:
return True
else:
return False
Use the builtin Python function list.count()
if text.count('.') < 2:
return True
It can be even shorter if instead of an if-else statement, you do
return text.count('.') < 2
Also, there are some errors in your function. All you need to do is
def check_dots(text):
return text.count('.') < 2
A correct and shorter version would be:
return text.count('.') <= 1
Python has a function called count()
You can do the following.
if text.count('.') < 2: #it checks for the number of '.' occuring in your string
return True
else:
return False
A shortcut would be:
return text.count('.')<2
Let's analyze the above statement.
in this part, text.count('.')<2: It basically says "I will check for periods that occur less than twice in the string and return True or False depending on the number of occurences." So if text.count('.') was 3, then that would be 3<2 which would become False.
another example. Say you want it to return False if a string is longer than 7 characters.
x = input("Enter a string.")
return len(x)>7
The code snippet len(x)>7 means that the program checks for the length of x. Let's pretend the string length is 9. In this case, len(x) would evaluate to 9, then it would evaluate to 9>7, which is True.
I shall now analyze your code.
def check_dots(text):
text = [] ################ Don't do this. This makes it a list,
# and the thing that you are trying to
# do involves strings, not lists. Remove it.
for char in text: #not needed, delete
if '.' < 2 in text: #I see your thinking, but you can use the count()
#to do this. so -> if text.count('.')<2: <- That
# will do the same thing as you attempted.
return True
else:
return False
I'm working my way through the MIT 6.00 class on OpenCourseWare and I'm having a little trouble with the lecture on recursion. I think I understand the basic idea, that you can break some problems down into smaller, repeatable problems. Where I'm having trouble is understanding how this works in the actual code. There is one specific example that I don't quite understand...
def toChars(s):
import string
s = string.lower(s)
ans = ''
for c in s:
if c in string.lowercase:
ans = ans + c
return ans
def isPal(s):
if len(s) <= 1:
return True
else:
return s[0] == s[-1] and isPal(s[1:-1])
def isPalindrome(s):
"""Returns True if s is a palindrome and False otherwise"""
return isPal(toChars(s))
This code is supposed to check if a string is a palindrome or not. I am a little lost on how isPal(s) works. Here's the steps as I'm reading them...
Check if s is one character or less, if it is return True.
If s has more than one character return a Boolean value (True or False) from the expression comparing the first and last character. So basically check if the first and last letter are the same.
AND return the result of the function run again with the first and last letters stripped from the string.
What is confusing me is the return s[0] == s[-1] and isPal(s[1:-1]) bit. I'm not sure I understand what it means to both return True/False based on the first expression AND return the function. Honestly I'm not even sure what returning the function actually means, I'm guessing it just runs the function again, but doesn't actually return any value. I also don't understand how this can actually tell if a string is a palindrome or not. Wouldn't it just keep hitting the second part of return s[0] == s[-1] and isPal(s[1:-1]) until the string, whether it was a palindrome or not, was reduced to 1 or 0 characters? The only thing I can think of is that returning a false value from return s[0] == s[-1] exits the function with a return of false? But I don't feel like that's actually how Python works, at least in my study so far I haven't run into anything saying that returning a False in a function would prevent the second part of the return statement from executing, which would call the function again regardless of whether the first and last letters where the same.
I'm kind of banging my head against the wall at this point, so any insight would be really appreciated!
Perhaps the best way is to go step by step with the values for that line that is confusing you.
return s[0] == s[-1] and isPal(s[1:-1])
Assuming the String you're testing for is "ABCDBA", here is the sequence of calls
return s[0] == s[-1] and isPal(s[1:-1]) #evaluate to ("A" == "A" and isPal("BCDB")) - need to go deeper
return s[0] == s[-1] and isPal(s[1:-1]) #evaluate ("B" == "B" and isPal("CD")) - need to go deeper
return s[0] == s[-1] and isPal(s[1:-1]) #returns ("C" == "D")
Note that in the last line we didn't evaluate for isPal because of short-circuiting - we know that False and X where X can be True or False is always False. Read more about that here.
At the last line we're 4 functions deep into isPal. Since the last line doesn't require us evaluate for isPal() again, we start "rising to the surface" back to the original invocation of isPal which was at
def isPalindrome(s):
"""Returns True if s is a palindrome and False otherwise"""
return isPal(toChars(s))
One clarification which can help you understand what's going on: return ...X and ...Y means: Compute the value of X. If the value is false, return it, otherwise compute the value of Y, and return it.
More info about the short-circuit, lazy behavior (i.e. not evaluating Y if not needed) of the and and or operators in Python: https://docs.python.org/release/2.7/reference/expressions.html#boolean-operations
So that statement doesn't make the function return two values (X and Y), but one value which is the logical AND of X and Y: it's true iff both X and Y are (evaluated to) true.
Also it's worth distinguishing function from function call. There can be many active calls to the same function, with different arguments, local variables etc., so their return values can also be different (as soon as they return). When a function calls itself, that's called recursion. When a function call returns, execution continues in the caller, which can be the same function (but of course a different, upper-level function call) in case of recursion.
If you run the isPal function on "heeeeh" with print statements:
def isPal(s):
if len(s) <= 1:
return True
else:
print s[0],s[-1]
return s[0] == s[-1] and isPal(s[1:-1])
print isPal("heeeeh")
You get:
h h
e e
e e
True
as each char at index s[0] and s[-1] are equal through each recursive call as the string is a palindrome.
Running it on "aeeee":
def isPal(s):
if len(s) <= 1:
return True
else:
print s[0],s[-1]
return s[0] == s[-1] and isPal(s[1:-1])
print isPal("aeeeeh")
You get :
a h
False
As as soon as s[0] and s-[1] are not equal the functions ends and returns False as it cannot be a palindrome if the chars are not equal.
I'll add my explanation, maybe this will help you.
First things first:
def toChars(s):
import string
s = string.lower(s)
ans = ''
for c in s:
if c in string.lowercase:
ans = ans + c
return ans
will simply take some text and turn it all to lower case, character by character.
Next is:
def isPal(s):
if len(s) <= 1:
return True
else:
return s[0] == s[-1] and isPal(s[1:-1])
To understand this part you ask yourself a question, what is a palindrome and what are ways to know if word or sentence is one.
In this example approach is: palindrome is word or sentence where its characters in same distance from beginning and from the end are the same characters.
So how to check that? Well, first if your word is just one letter or shorter then it's a palindrome.
Then you check if letters at the beginning and the end of your word are the same.
You remember the result as true or false and strip your word from letters you already checked and repeat the process until your word is 1 or less characters long and check if every result of comparison was true.
Example:
Foob->foob
f=b? NO, false
Example 2:
Fbof->fbof
f=f? Yes, true,
bo
b=o? No, false.
true and false = false, so the word is not a palindrome
Example 3:
Fobof->fobof
f=f? Yes, true,
obo,
o=o? Yes, true
b
len(b) = <=1 ? Yes, true,
true and true and true = true, so the word is a palindrome.
Hope it helps, ask if you need more thorough explanation, I'll try to help, no worries, i struggled with recursion at the beginning as well.