is this a good approach for anagram problem? - python

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.

Related

How to count number of duplicates of item in string?

Suppose I have an string: s = "hello2020"
How can I create a program that returns the number of duplicates in the string? In this instance, the program would return 3 as the letter "l" appears more than once, and the numbers "2" and "0" also appear more than once.
Thanks in advance.
EDIT: So far, I have tried: return len([x for x in set(s) if s.count(x) > 1]), but it fails two of the testcases. Therefore, I am looking for an alternative solution.
from collections import Counter
def do_find_duplicates(x):
dup_chars = 0
for key,val in Counter(x).items():
if val > 1: dup_chars += 1
print(dup_chars)
do_find_duplicates('hello2020')
One Liner Solution, convert string to set then subtract length of string by converted set string
def duplicate_count(string):
return len(string) - len(set(string))
print(duplicate_count("hello2020"))
#3

Deciding whether a string is a palindrome

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.')

Is there anyway I can check whether part of a string in python is somewhere in another string? (beginner)

Let's say we have a string 'abc' and another string 'bcd'. If I do 'abc' in 'bcd' it will return false. I want to say 'if there is a character of 'abc' in the string 'bcd' than return true. (python)
edit: thank you for the spelling changes. It makes me feel dumb. They were typos though.
I have tried iterating through the string using for loops, but this is clunky and I am assuming it is not good practice. Anyway I couldn't make it flexible enough for my needs.
import random
symb1 = random.choice('abc#') # I am trying to test if it chose AL one
# symbol
symb2 = random.choice('abc!')
mystring = (symb1+symb2) #lets say mystring is 'a!'
if mystring in '#!' # I want to test here somehow if part of mystring is
# in #!
I want it to output true, and the output is false. I understand why, I just need help creating a way to test for the symbol in mystring
Iterate one of the Strings while doing in checks:
any(c in "#!" for c in mystring)
"Is there any c from mystring in '#!'?"
You could use list comprehensions.
Example;
>>> a = 'abc'
>>> b = 'bcd'
>>> [letter for letter in a if letter in b] # list comprehensions
['b', 'c']
>>> any(letter for letter in a if letter in b) # generator expression
True
as mentioned by #asikorski; change to use generator expression so the loop stops on the first match.
Okay the comprehensions made more sense but I decided to do a more expanded for loop. I found a way to make it less terrible. I'm just teaching python to a friend and I want him to be able to read the code more easily. here's the section of code in the program.
def punc():
characters = int(input('How long would you like your password to be? : '))
passlist = []
for x in range(characters):
add = random.choice(string.ascii_letters + '#####$$$$$~~~~!!!!!?????')
passlist.append(add)
l = []
for x in passlist:
if x in ['#','$','~','!','?']:
l.append(0)
if len(l) == 0:
punc()
else:
print(''.join(passlist))
There are few solutions:
the best one (both effective and pythonic):
if any(char in 'bcd' for char in 'abc'):
...
Uses generator + built-in any. It does not have to create a list with list comprehension and doesn't waste time to gather all the letters that are in both of strings, so it's memory effective.
the boring one (~ C style):
def check_letters(word1, word2):
for char in word1:
if char in word2:
return True
reeturn False
if check_letters('abc', 'bcd'):
...
Quite obvious.
the fancy one:
if set(list('abc')) & set(list('bcd')):
...
This one uses some tricks. list converts string to a list of letters, set creates a set of letters from the list. Then intersetion of two sets is created with & operator and if's condition evaluates to True if there's any element in the intersection.
It's not very effective, though; it has to create two lists and two sets.

Python find if strings are anagram of each other string

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)

Find Longest Word Not Working

So I was exploring on coderbyte.com and one of the challenges is to find the longest word in a string. My code to do so is the following:
def LongestWord(sen):
current="";
currentBest=0
numberOfLettersInWord=0
longestWord=0
temp=sen.split()
for item in temp:
listOfCharacters=list(item)
for currentChar in listOfCharacters:
if currentChar.isalpha():
numberOfLettersInWord+=1
if numberOfLettersInWord>longestWord:
longestWord=numberOfLettersInWord
numberOfLettersInWord=0
currentBest=item
z = list(currentBest)
x=''
for item in z:
if item.isalpha(): x+=item
return x
testCase="a confusing /:sentence:/ this"
print LongestWord(testCase)
when testCase is "a confusing /:sentence:/"
The code returns confusing, which is the correct answer. But when the test case is the one in the current code, my code is returning 'this' instead of 'confusing'
Any ideas as to why this is happening?
I know that this is not the answer to your question, but this is how I would calculate the longest word. And not sharing it, wouldn't help you, either:
import re
def func(text: str) -> str:
words = re.findall(r"[\w]+", text)
return max(words, key=len)
print(func('a confusing /:sentence:/ this'))
Let me suggest another approach, which is more modular and more Pythonic.
Let's make a function to measure word length:
def word_length(w):
return sum(ch.isalpha() for ch in w)
So it will count (using sum()) how many characters there are for which .isalpha() is True:
>>> word_length('hello!!!')
5
>>> word_length('/:sentence:/')
8
Now, from a list of words, create a list of lengths. This is easily done with map():
>>> sen = 'a confusing /:sentence:/ this'.split()
>>> map(word_length, sen)
[1, 9, 8, 4]
Another builtin useful to find the maximum value in a list is max():
>>> max(map(word_length, sen))
9
But you want to know the word which maximizes the length, which in mathematical terms is called argument of the maximum.
To solve this, zip() the lengths with the words, and get the second argument found by max().
Since this is useful in many cases, make it a function:
def arg_max(func, values):
return max(zip(map(func, values), values))[1]
Now the longest word is easily found with:
>>> arg_max(word_length, sen)
'confusing'
Note: PEP-0008 (Style Guide for Python Code) suggests that function names be lower case and with words separated by underscore.
You loop through the words composing the sentence. However, numberOfLettersInWord is never reseted so it keeps increasing while you iterate among the words.
You have to set the counter to 0 each time you start a new word.
for item in temp:
numberOfLettersInWord = 0
It solves your issue as you can see: https://ideone.com/y1cmHX
Here's a little function I just wrote that will return the longest word, using a regular expression to remove non alpha-numeric characters
import re
def longest_word(input):
words = input.split()
longest = ''
for word in words:
word = re.sub(r'\W+', '', word)
if len(word) > len(longest):
longest = word
return longest
print(longest_word("a confusing /:sentence:/ this"))

Categories