Find all letters of a string in another string - python

I'm working on a project that find all anagrams of a word with many others words.
For that I need a function that takes 2 strings and return True if all letters of str1 are in str2.
I made this code:
def almost_anagram(str1, str2):
tmp = list(str2)
for i in str1:
if i in tmp:
tmp.remove(i)
else:
return False
return True
For exemple:
almost_anagram("OLLE", "HELLO") = True
almost_anagram("OOLE", "HELLO") = False
But is there a better/faster way to do that ?

You can use Counter, which essentially represents a multi-set:
import collections
def almost_anagram(word1, word2):
counter1 = collections.Counter(word1)
counter2 = collections.Counter(word2)
return counter1 - counter2 == {}
# alternatively:
# return all(counter2[k] >= v for k, v in counter1.items())
# return counter1 & counter2 == counter1
The code could be simplified to return counter1 < counter2 if Counter supported subset-testing with < like sets do, but unfortunately it doesn't.
Output:
>>> almost_anagram("OLLE", "HELLO")
True
>>> almost_anagram("OOLE", "HELLO")
False

With collections.Counter object to get letter counts at once:
import collections
def almost_anagram(str1, str2):
str1_cnt, str2_cnt = collections.Counter(str1), collections.Counter(str2)
return all(k in str2_cnt and str2_cnt[k] == v
for k,v in str1_cnt.items())
Test:
print(almost_anagram("OLLE", "HELLO")) # True
print(almost_anagram("OOLE", "HELLO")) # False

Using a list comprehension with all should be faster.
Ex:
def almost_anagram(str1, str2):
return all(str2.count(i) >= str1.count(i) for i in set(str1))
print(almost_anagram("OLLE", "HELLO"))
print(almost_anagram("OOLE", "HELLO"))
Output:
True
False

i prefer using count() function and by the way you don't need to convert the string into list:
def almost_anagram(str1, str2):
for i in str1:
if(str1.count(i)==str2.count(i)):
pass
else:
return False
return True
print(almost_anagram('olle','hello'))
print(almost_anagram('oole','hello'))
output:
True
False

Related

finding the longest common prefix of elements inside a list

I have a sequence print(lcp(["flower","flow","flight", "dog"])) which should return fl. Currently I can get it to return flowfl.
I can locate the instances where o or w should be removed, and tried different approaches to remove them. However they seem to hit syntax issue, which I cannot seem to resolve by myself.
I would very much appreciate a little guidance to either have the tools to remedy this issue my self, or learn from a working proposed solution.
def lcp(strs):
if not isinstance(strs, list) or len(strs) == 0:
return ""
if len(strs) == 1:
return strs[0]
original = strs[0]
original_max = len(original)
result = ""
for _, word in enumerate(strs[1:],1):
current_max = len(word)
i = 0
while i < current_max and i < original_max:
copy = "".join(result)
if len(copy) and copy[i-1] not in word:
# result = result.replace(copy[i-1], "")
# result = copy[:i-1]
print(copy[i-1], copy, result.index(copy[i-1]), i, word)
if word[i] == original[i]:
result += word[i]
i += 1
return result
print(lcp(["flower","flow","flight", "dog"])) # returns flowfl should be fl
print(lcp(["dog","car"])) # works
print(lcp(["dog","racecar","car"])) # works
print(lcp([])) # works
print(lcp(["one"])) # works
I worked on an alternative which does not be solve removing inside the same loop, adding a counter at the end. However my instincts suggest it can be solved within the for and while loops without increasing code bloat.
if len(result) > 1:
counter = {char: result.count(char) for char in result}
print(counter)
I have solved this using the below approach.
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
N = len(strs)
if N == 1:
return strs[0]
len_of_small_str, small_str = self.get_min_str(strs)
ans = ""
for i in range(len_of_small_str):
ch = small_str[i]
is_qualified = True
for j in range(N):
if strs[j][i] != ch:
is_qualified = False
break
if is_qualified:
ans += ch
else:
break
return ans
def get_min_str(self, A):
min_len = len(A[0])
s = A[0]
for i in range(1, len(A)):
if len(A[i]) < min_len:
min_len = len(A[i])
s = A[i]
return min_len, s
Returns the longest prefix that the set of words have in common.
def lcp(strs):
if len(strs) == 0:
return ""
result = strs[0]
for word in strs[1:]:
for i, (l1, l2) in enumerate(zip(result, word)):
if l1 != l2:
result = result[:i]
break
else:
result = result[:i+1]
return result
Results:
>>> print(lcp(["flower","flow","flight"]))
fl
>>> print(lcp(["flower","flow","flight", "dog"]))
>>> print(lcp(["dog","car"]))
>>> print(lcp(["dog","racecar","car"]))
>>> print(lcp([]))
>>> print(lcp(["one"]))
one
>>> print(lcp(["one", "one"]))
one
You might need to rephrase your goal.
By your description you don't want the longest common prefix, but the prefix that the most words have in common with the first one.
One of your issues is that your tests only test one real case and four edgecases. Make some more real examples.
Here's my proposition: I mostly added the elif to check if we already have a difference on the first letter to then discard the entry.
It also overwrites the original to rebuild the string based on the common prefix with the next word (if there are any)
def lcp(strs):
if not isinstance(strs, list) or len(strs) == 0:
return ""
if len(strs) == 1:
return strs[0]
original = strs[0]
result = ""
for word in strs[1:]:
i = 0
while i < len(word) and i < len(original) :
if word[i] == original[i]:
result += word[i]
elif i == 0:
result = original
break
i += 1
original = result
result = ""
return original
print(lcp(["flower","flow","flight", "dog"])) # fl
print(lcp(["shift", "shill", "hunter", "shame"])) # sh
print(lcp(["dog","car"])) # dog
print(lcp(["dog","racecar","car"])) # dog
print(lcp(["dog","racecar","dodge"])) # do
print(lcp([])) # [nothing]
print(lcp(["one"])) # one

trying to do a python for code without using the "in" operator and it's not working

Simple assignment from uni, supposed to find a word in a list and print if true without using the "in" operator. I've instead used = and it's just not printing anything. Is there something wrong with the code or is there an alternative I'm missing?
wordlist = ["hi", "many","way","photo","mobile"]
def word_in_list_for(words,word):
for word = wordlist:
if word == wordlist:
return True
else:
continue
print(word_in_list_for(wordlist,"bild"))
def word_in_wordlist(words, word):
i = 0
while i < len(words):
if words[i] == word:
return True
i += 1
return False
Testing:
In [221]: wordlist = ["hi", "many","way","photo","mobile"]
In [222]: print(word_in_wordlist(wordlist,"bild"))
False
In [223]: print(word_in_wordlist(wordlist,"photo"))
True
Do this:
wordlist = ["hi", "many","way","photo","mobile"]
def word_in_list_for(words,word):
i = 0
while wordlist[i]:
if word == wordlist[i]:
return True
else:
i += 1
print(word_in_list_for(wordlist,"bild"))
Tell me if this works
You can use set
wordlist = ["hi", "many","way","photo","mobile"]
def word_in_list_for(words,word):
words_set = set(words)
return {word}.issubset(words_set)
print(word_in_list_for(wordlist,"bild"))
False
You should use the indexing. I have written an working example from your code. You can find the explanation as comments in script.
Code:
wordlist = ["hi", "many", "way", "photo", "mobile"]
def word_in_list_for(words, word):
for index in range(len(words)): # Get the indexes of your list.
if word == words[index]: # If the value of the index is the same as word
return True # Return True
return False # The the elem is not is the list return False
print(word_in_list_for(wordlist, "bild"))
Successful test (word_in_list_for(wordlist, "mobile")) :
>>> python3 test.py
True
Unsuccessful test (word_in_list_for(wordlist, "bild")) :
>>> python3 test.py
False

Writing a function which accepts two strings and returns True in python 3

Write a python function, check_anagram() which accepts two strings and returns True, if one string is an anagram of another string. Otherwise returns False.
The two strings are considered to be an anagram if they contain repeating characters but none of the characters repeat at the same position. The length of the strings should be the same.
Note: Perform case insensitive comparison wherever applicable.
This is my code:
def check_anagram(data1,data2):
first = data1.lower()
second = data2.lower()
d1 = []
d2 = []
for i in range(0, len(first)):
d1.append(first[i])
for i in range(0, len(second)):
d2.append(second[i])
for_check1 = sorted(d1)
for_check2 = sorted(d2)
if (for_check1 != for_check2):
return False
count = 0
if (len(d1) == len(d2)):
for i in d1:
for j in d2:
if(i == j):
a = d1.index(i)
b = d2.index(j)
if(a == b):
return False
else:
count += 1
if(count == len(first)):
return True
else:
return False
print(check_anagram("Schoolmaster", "Theclassroom"))
The output I am getting is "False"
Although this program is giving relevant output for string values like {silent, listen}{Moonstarrer, Astronomer}{apple, mango} but not for the above two strings(in code)
What cases am I missing in this code?? How to rectify this thing?
Your function could be simplified as:
def check_anagram(data1, data2):
data1 = data1.lower()
data2 = data2.lower()
if sorted(data1) != sorted(data2):
return False
return all(data1[i] != data2[i] for i in range(len(data1)))
Which actually works for the case you specified.
your code is correct just write len(second) instead of count.
def check_anagram(data1,data2):
first = data1.lower()
second = data2.lower()
d1 = []
d2 = []
for i in range(0, len(first)):
d1.append(first[i])
for i in range(0, len(second)):
d2.append(second[i])
for_check1 = sorted(d1)
for_check2 = sorted(d2)
if (for_check1 != for_check2):
return False
count = 0
if (len(d1) == len(d2)):
for i in d1:
for j in d2:
if(i == j):
a = d1.index(i)
b = d2.index(j)
if(a == b):
return False
else:
count += 1
if(len(second) == len(first)):
return True
else:
return False
print(check_anagram("Schoolmaster", "Theclassroom"))
This program of mine is clearing all possible test cases.
def check_anagram(data1,data2):
data1=data1.lower()
data2=data2.lower()
if(len(data1)==len(data2)):
if(sorted(data1)!=sorted(data2)):
return False
else:
if(data1[i]!=data2[i] for i in range(len(data1))):
return True
else:
return False
else:
return False
print(check_anagram("eat","tea"))

Creating an anagram checker

So I have been able to create the following program which compares two strings to see if they are anagrams of each other.
def anagrams( string1, string2 ):
if sorted(string1.lower()) == sorted(string2.lower()):
return True
else:
return False
However, my issue is that I wish to not return a True value should both input strings be the exact same. For example:
anagrams('silent','silent')
This outputs True but I do not want it to do that, what changes should I make to implement this?
Just check if the strings are different:
def anagrams(string1, string2):
if sorted(string1.lower()) == sorted(string2.lower()) and string1.lower() != string2.lower():
return True
else:
return False
result = anagrams('silent', 'silent')
print(result)
Output
False
You could use Counter instead of sorted:
from collections import Counter
def anagrams(string1, string2):
if string1.lower() != string2.lower() and Counter(string1.lower()) == Counter(string2.lower()):
return True
else:
return False
print(anagrams('silent', 'silent'))
print(anagrams('silent', 'sitlen'))
Output
False
True
UPDATE
As suggested by #RoadRunner, you could do:
from collections import Counter
def anagrams(string1, string2):
ls1, ls2 = string1.lower(), string2.lower()
return ls1 != ls2 and Counter(ls1) == Counter(ls2)
print(anagrams('silent', 'silent'))
print(anagrams('silent', 'sitlen'))
def anagrams( string1, string2 ):
if sorted(string1.lower()) == sorted(string2.lower()) and string1.lower() != string2.lower():
return True
else:
return False
print(anagrams('silent','silent'))

Backtrack Algorithm To Check Strings form Matrix

I have list:
words = ["ALI", "SIN", "ASI", "LIR", "IRI", "INI", "KAR"]
I want to check if they form matrix such as this:
and return my solution as a list like:
solution = ["ALI", "SIN", "IRI"]
I have come up with this code:
words=["ALI", "SIN", "ASI", "LIR", "IRI", "INI", "KAR"]
solution =[]
failedsolutions = []
def Get_next_word():
while True:
for word in words:
if (word in solution) == False:
solution.append(word)
if (solution in failedsolutions) == False:
return False
else:
solution.pop(len(solution) - 1 )
return True
def Check_word_order():
checker = True
for i in range(len(solution)):
for j in range(len(words[0])):
for word in words:
if solution[i][j] == word[j]:
checker = False
else:
checker = True
if checker == False:
return checker
def main():
while True:
Get_next_word()
check = Check_word_order()
if check is False:
#Backtrack
failedsolutions.append(solution)
solution.pop(len(solution) - 1 )
# else:
# solution.append()
print(solution)
main()
I am tired and no longer be able to debug my code. Help will be appreciated.
Thank you
ps: I advice not fixing my code if there is a better way to it all.
You can use this simple recursive function which will analyze all possible groups:
import itertools
import copy
words = ["ALI", "SIN", "ASI", "LIR", "IRI", "INI", "KAR"]
def get_pairs(word_group, current_words, found):
if not current_words:
return found
new_group = list(word_group)+[current_words[0]]
if all(''.join(i) in words and ''.join(i) not in new_group for i in zip(*new_group)):
return get_pairs(word_group, current_words[1:], new_group)
return get_pairs(word_group, current_words[1:], found)
starting_pairs = [i for i in itertools.combinations(words, 2)]
final_listing = filter(lambda x:x, [get_pairs(i, copy.deepcopy(words), []) for i in starting_pairs])
Output:
[['ALI', 'SIN', 'IRI'], ['ASI', 'LIR', 'INI']]
Which yields all combinations of valid matrices.
Or, without using itertools:
def get_combos(word, new_words):
if new_words[1:]:
new_list = [(yield (word, i)) for i in new_words if i != word]
for b in get_combos(new_words[0], new_words[1:]):
yield b
starting_pairs = get_combos(words[0], words[1:])
final_listing = filter(lambda x:x, [get_pairs(i, copy.deepcopy(words), []) for i in starting_pairs])

Categories

Resources