I am trying to solve the above interview question to check if string are anagram of each other. The implementation is as follows:
NO_OF_CHARS = 256
def areAnagram(str1, str2):
count = [0] * NO_OF_CHARS
i = 0
while (i in str1) and (i in str2):
count[ord(i)]+=1
i += 1
if len(str1) != len(str2):
return 0
for i in xrange(NO_OF_CHARS):
if count[i]:
return 0
return 1
str1 = "geeksforgeeks"
str2 = "forgeeksgeeks"
if areAnagram(str1, str2):
print "The two strings are anagram of each other"
else:
print "The two strings are not anagram of each other"
I am getting the following error while running the code:
TypeError: 'In <string> requires string as left operand
Am I doing something wrong in the while loop? Also, how can I avoid to use i = 0 statement? Thanks.
An easy way to see if strings are comprised of the same characters is to compare them as sorted lists:
def is_anagram(src, trgt):
"""
Determine if trgt is an anagram of src
:param src: (str)
:param trgt: (str)
:returns: (bool) True if trgt is an anagram of src; else False
"""
return sorted(src) == sorted(trgt)
If you want to go for counting the characters, you need to make counts for both strings and compare them
NO_OF_CHARS = 256
def areAnagram(str1, str2):
if len(str1) != len(str2):
return 0
count = [0] * NO_OF_CHARS
for c1,c2 in zip(str1,str2):
count[ord(c1)] +=1
count[ord(c2)] -=1
return all(not c for c in count)
I moved checking the length of strings to the beginning of the method for efficiency and clarity
EDIT: Updated my answer according to Blckknght's comment
The canonical way to do this in Python is to use collections.Counter:
from collections import Counter
def areAnagram(str1, str2):
return Counter(str1) == Counter(str2)
This should take O(N) space and time (where N is max(len(str1), len(str2))). But do be aware that even though this code's asymptotic performance is better, it may still be slower for short strings than a version using sorted. Python's sort code is very fast!
If you're likely to be using the function to compare very-unlike strings, you could perhaps it up a little bit with a special case checking the string lengths before counting:
def areAnagram(str1, str2):
return len(str1) == len(str2) and Counter(str1) == Counter(str2)
Related
Hello everyone i am beginner and i wanted to ask is this a good approach to not sort characters in (is anagram) problem i saw some tutorials and they sorted the strings but i wrote an algorithm without sorting the string and it works very well on the goal.
def is_anagram(str1, str2):
if len(str1) != len(str2):
return False
else:
for i in range(len(str2)):
if str1[i] not in str2 or str2[i] not in str1:
return False
return True
return False
print(is_anagram("hey", "hey"))
it is very simple than tutorials and as i checked its running time was 1 sec on about two million length of string.
Thank You need your consideration.
Try this:
from collections import Counter
def is_anagram(str1, str2):
return Counter(str1) == Counter(str2)
Examples:
>>> is_anagram("pat", "tap")
True
>>> is_anagram("hell", "heel")
False
>>>
If you want to write it completely on your own, you can create a dict for each string, where the keys are the individual characters and the values are the number of occurrences of that character. Then compare dicts.
I am new to coding and try to extract and print the none digits. I've written 2 different codes but cannot combine them. I would appreciate some advices. (i tried using *args but didn't work)
def SumOfDigits(str1):
sum_digit = 0
for x in str1:
if x.isdigit():
z = int(x)
sum_digit += z
print("The sum of digits operation is", sum_digit, end=".")
return
def SumOfDigits(input):
valids = []
for character in input:
if character.isalpha():
valids.append(character)
print("The extracted non-digits are:", ''.join(valids))
return
El. Nik, i believe that those 2 function you not be merged as they are doing different behavior, which lead to a more complex function, that might get a bit confusing.
Anyway, what you want is to merge your two for loop into a single one. Since str.isalpha matches do not collide with str.isdigit ones, you can safely use a if statement followed by an elif to check every charater and apply the wanted behavior.
Then, you simply return the 2 results as a tuple.
def digit_extraction(string):
sum_digits = 0
extracted_alphas = ""
for char in string:
if char.isdigit():
sum_digits += int(char)
elif char.isalpha():
extracted_alphas += char
return sum_digits, extracted_alphas
Here is a quick example:
>>> digit_extraction("1a2b3c4d5e6f7g8h9i")
(45, 'abcdefghi')
If you dont know how multiple return value works in python, i'd suggest to check the following tutorial:
https://datagy.io/python-return-multiple-values/
To get into something more advanced, separating the 2 function would allow for writting something considered as more pythonic:
def sum_of_digits(string):
return sum(int(c) for c in string if c.isalpha())
and
def extract_alphas(string):
return ''.join(filter(str.isalpha, c))
This is a python question. Answer should be with O(n) time complexity and use no additional memory. As input i get a string which should be classified as palindrome or not (palindrome is as word or a phrase that can be read the same from left to right and from right to left, f.e "level"). In the input there can be punctuation marks and gaps between words.
For example "I. did,,, did I????" The main goal is to decide whether the input is a palindrome.
When I tried to solve this question i faced several challenges. When I try to delete non letter digits
for element in string:
if ord(element) not in range(97, 122):
string.remove(element)
if ord(element) == 32:
string.remove(element)
I use O(n^2) complexity, because for every element in the string i use remove function, which itself has O(n) complexity, where n is the length of the list. I need help optimizing the part with eliminating non letter characters with O(n) complexity
Also, when we get rid of spaces as punctuation marks I know how to check whether a word is a palindrome, but my method uses additional memory.
Here is your O(n) solution without creating a new string:
def is_palindrome(string):
left = 0
right = len(string) - 1
while left < right:
if not string[left].isalpha():
left += 1
continue
if not string[right].isalpha():
right -= 1
continue
if string[left] != string[right]:
return False
left += 1
right -= 1
return True
print(is_palindrome("I. did,,, did I????"))
Output:
True
I'm assuming you mean you want to test if a string is a palindrome when we remove all punctuation digits from the string. In that case, the following code should suffice:
from string import ascii_letters
def is_palindrome(s):
s = ''.join(c for c in s if c in ascii_letters)
return s == s[::-1]
# some test cases:
print(is_palindrome('hello')) # False
print(is_palindrome('ra_ceca232r')) # True
Here's a one-liner using assignment expression syntax (Python 3.8+):
>>> s = "I. did,,, did I????"
>>> (n := [c.lower() for c in s if c.isalpha()]) == n[::-1]
True
I mostly showed the above as a demonstration; for readability's sake I'd recommend something more like SimonR's solution (although still using isalpha over comparing to ascii_letters).
Alternatively, you can use generator expressions to do the same comparison without allocating O(n) extra memory:
def is_palindrome(s):
forward = (c.lower() for c in s if c.isalpha())
back = (c.lower() for c in reversed(s) if c.isalpha())
return all(a == b for a, b in zip(forward, back))
Note that zip still allocates in Python 2, you'll need to use itertools.izip there.
Will this help:
word = input('Input your word: ')
word1 = ''
for l in word:
if l.isalnum():
word1 += l
word2=''
for index in sorted(range(len(word1)),reverse=True):
word2+=word1[index]
if word1 == word2:
print('It is a palindrone.')
else:
print('It is not a palindrone.')
I implemented string contraction which lists the characters of a string with their respective counts. So, for example, the string "aadddza" becomes "a2d3z1a1". Can you offer suggestions on making what I have Pythonic?
def string_contraction(input_string):
if type(input_string) != str:
print("Not a string")
return
input_string = str.lower(input_string)
prev = input_string[0]
s = ""
i = 0
for lett in input_string:
if lett == prev:
i += 1
else:
s += prev+str(i)
prev = lett
i = 1
s += lett+str(i)
return(s)
The itertools.groupby function can do the "hard part" for you. Here's an example ("rle" is short for "run length encoding"):
def rle(s):
from itertools import groupby
return "".join(letter + str(len(list(group)))
for letter, group in groupby(s))
Then:
>>> rle("aadddza")
'a2d3z1a1'
It may take a while staring at the docs to figure out how this works - groupby() is quite logical, but not simple. In particular, the second element of each two-tuple it generates is not a list of matching elements from the original iterable, but is itself an iterable that generates a sequence of matching elements from the original iterable. Your application only cares about the number of matching elements. So the code uses list(group) to turn the iterable into a list, and then applies len() to that to get the number.
I'm trying to check if 2 strings are anagrams. This solution is simple, but not efficient (Ologn) I know I could use Collections and Counter, then compare the occurrence of each character, but I'm trying to avoid any modules for an interview. What would be the fastest way to solve this problem? (Perhaps, checking occurrence of each character?)
def check(word1,word2):
return sorted(word1)==sorted(word2)
Your code doesn't even return a correct value. This one-liner is O(n log n):
return sorted(word1) == sorted(word2)
For an O(n) solution, you can count all characters:
from collections import Counter
# ...
def check(a, b)
return Counter(a) == Counter(b)
Without collections it is much longer:
def check(a, b):
chars = dict.fromkeys(a + b, 0)
for c in a:
chars[c] += 1
for c in b:
chars[c] -= 1
return not any(chars.values())
This code does the following:
chars = dict.fromkeys(a + b, 0): Creates a dict, which has all the occurring characters in either word as keys set to 0.
for c in a: chars[c] += 1: this will iterate over a and count the occurrences of each character in it. chars now contains the count of separate characters, (and some zeroes for characters in b but not a)
for c in b: chars[c] -= 1: much the same as before, but instead this will subtract the character counts of b from chars
return not any(chars.values()): chars['h'] == 0 if and only if a and b has the same amount of 'h'. This line checks if chars has only zeroes as values, meaning that all characters have the same count in both inputs. (as any returns if there is any truthy value in the sequence. 0 is falsy, every other integer is truthy.)
Both lists get iterated over once. Assuming O(1) access time for dictionaries makes the whole algorithm run in O(n) time (where n is the total length of the inputs). Space complexity is O(n) too (all characters can be distinct). Don't make that mistake when they ask you complexity. It's not necessary time complexity.
Here's a nice option from http://interactivepython.org/runestone/static/pythonds/AlgorithmAnalysis/AnAnagramDetectionExample.html:
def anagramSolution(s1,s2):
TABLE_SIZE = 128
c1 = [0]*TABLE_SIZE
c2 = [0]*TABLE_SIZE
for ch in s1:
pos = ord(ch)
c1[pos] = c1[pos] + 1
for ch in s2:
pos = ord(ch)
c2[pos] = c2[pos] + 1
j = 0
stillOK = True
while j<TABLE_SIZE and stillOK:
if c1[j]==c2[j]:
j = j + 1
else:
stillOK = False
return stillOK
This runs in O(n). Essentially, you loop over both strings, counting the occurrences of each letter. In the end, you can simply iterate over each letter, making sure the counts are equal.
As noted in the comments, this will have a harder time scaling for unicode. If you expect unicode, you would likely want to use a dictionary.
I'd write it like this without imports:
def count_occurences(mystring):
occs = {}
for char in mystring:
if char in occs:
occs[char] += 1
else:
occs[char] = 1
return occs
def is_anagram(str1, str2):
return count_occurences(str1) == count_occurences(str2)
Or, if you can use imports, just not a Counter, use a defaultdict:
from collections import defaultdict
def count_occurences(mystring):
occs = defaultdict(int)
for char in mystring:
occs[char] += 1
return occs
def is_anagram(str1, str2):
return count_occurences(str1) == count_occurences(str2)