I am tasked to write a function that returns whether two strings are substitution ciphers of each other. It is assumed that one isn't given a key. The output is expected to return True or False.
Here is what I have written so far on this (borrowed from a CodeFights question). The idea is to append the counts of each element in the string and add it to the string1count and string2count variables. Then, compare the counts at each index, and if they are not equal, we can assume that it is not a valid substitution cipher since each element in the array needs to have the same number of corresponding of characters in order to be a substitution cipher.
def isSubstitutionCipher(string1, string2):
string1count = []
string2count = []
for i in range(0,len(string1)):
string1count.append(string1.count(string1[i]))
for i in range(0,len(string2)):
string2count.append(string2.count(string2[i]))
for i in range(0,len(string1count)):
if string1count.count(string1count[i])!=string2count.count(string1count[i]):
return False
return True
Does anyone else have other proposals on how to solve this very general question / problem statement?
you could try to re-create the subsitution:
def isSubstitutionCipher(string1, string2):
if len(string1) != len(string2):
return False
subst = {}
for c1, c2 in zip(string1, string2):
if c1 in subst:
if c2 != subst[c1]:
return False
else:
if c2 in subst.values():
return False
subst[c1] = c2
return True
for all the characters you have already seen, make sure the substitution matches. for the new ones: store them in the substitution and make sure they are not already a substitution target.
this will return False at the first character that does not match.
Here is a variation on hiro's excellent answer:
def is_sub(s,t):
if len(s) != len(t):return False
d = dict(zip(s,t))
return t == ''.join(d[c] for c in s)
We can use word patterns to check if one string is the ciphertext of another.
word pattern: first letter gets the number 0 and the first occurrence of each different letter after that gets the next number.
advantage is this has O(n) complexity
Code
def isSubstitutionCipher(s1, s2):
def word_pattern(s):
' Generates word pattern of s '
seen, pattern = {}, []
for c in s:
seen.setdefault(c, len(seen))
pattern.append(seen[c])
return pattern
return word_pattern(s1) == word_pattern(s2) # related by ciphertext if same word patterns
Test
'
print(isSubstitutionCipher('banana', 'cololo')) # Output: True
print(isSubstitutionCipher('dog', 'cat') # Output: True
print(isSubstitutionCipher('banana', 'cololl') # Output: False
Given 2 strings, each containing a DNA sequence, the function returns a bool to show if a contiguous sub-fragment of length 5 or above exists in string1 that can pair w/ a fragment of str2.
Here is what I tried using the functions "complement" and "reverese_complement" that I created but it doesn't give the right result:
def complement5(sequence1,sequence2):
for i in range(len(sequence1) - 4):
substr1 = sequence1[i:i+5]
if complement(substr1) in sequence2 or reverese_complement(substr1) in sequence2:
print(f'{substr1, complement(substr1), reverese_complement(substr1)} in {sequence2}')
return True
else:
return False
Then when I try:
complement5('CAATTCC','CTTAAGG')
it gives False instead of True
I personally would try to identify the whole complement first, then try to use the python function find(). See the example below
s = "This be a string"
if s.find("is") == -1:
print("No 'is' here!")
else:
print("Found 'is' in the string.")
So... for your genetics problem here:
Given str1 = "AGAGAG..."
Given str2 = "TCTCTC..."
Iterate through str1 sub-chunks (of length 5?) with a for loop and identify possible complements.
str1-complement-chunk = "TCTCTC" #use a method here. Obviously i'm simplifiying.
if (str2.find(str1-complement-chunk ) == -1):
print("dang couldn't find it")
else:
print('There exists a complement at <index>')
Additionally, you may reverse the chunk if you need to do so. I believe it can be accessed with str1[:-1], or something like that idk tho.
This question already has answers here:
Anagram of String 2 is Substring of String 1
(5 answers)
Closed 5 years ago.
EDIT: Posting my final solution because this was a very helpful thread and I want to add some finality to it. Using the advice from both answers below I was able to craft a solution. I added a helper function in which I defined an anagram. Here is my final solution:
def anagram(s1, s2):
s1 = list(s1)
s2 = list(s2)
s1.sort()
s2.sort()
return s1 == s2
def Question1(t, s):
t_len = len(t)
s_len = len(s)
t_sort = sorted(t)
for start in range(s_len - t_len + 1):
if anagram(s[start: start+t_len], t):
return True
return False
print Question1("app", "paple")
I am working on some practice technical interview questions and I'm stuck on the following question:
Find whether an anagram of string t is a substring of s
I have worked out the following two variants of my code, and a solution to this I believe lies in a cross between the two. The problem I am having is that the first code always prints False., regardless of input. The second variation works to some degree. However, it cannot sort individual letters. For example t=jks s=jksd will print True! however t=kjs s=jksd will print False.
def Question1():
# Define strings as raw user input.
t = raw_input("Enter phrase t:")
s = raw_input("Enter phrase s:")
# Use the sorted function to find if t in s
if sorted(t.lower()) in sorted(s.lower()):
print("True!")
else:
print("False.")
Question1()
Working variant:
def Question1():
# Define strings as raw user input.
t = raw_input("Enter phrase t:")
s = raw_input("Enter phrase s:")
# use a loop to find if t is in s.
if t.lower() in s.lower():
print("True!")
else:
print("False.")
Question1()
I believe there is a solution that lies between these two, but I'm having trouble figuring out how to use sorted in this situation.
You're very much on the right track. First, please note that there is no loop in your second attempt.
The problem is that you can't simply sort all of s and then look for sorted(t) in that. Rather, you have to consider each len(t) sized substring of s, and check that against the sorted t. Consider the trivial example:
t = "abd"
s = "abdc"
s trivially contains t. However, when you sort them, you get the strings abd and abcd, and the in comparison fails. The sorting gets other letters in the way.
Instead, you need to step through s in chunks the size of t.
t_len = len(t)
s_len = len(s)
t_sort = sorted(t)
for start in range(s_len - t_len + 1):
chunk = s[start:start+t_len]
if t_sort == sorted(chunk):
# SUCCESS!!
I think your problem lies in the "substring" requirement. If you sort, you destroy order. Which means that while you can determine that an anagram of string1 is an anagram of a substring of string2, until you actually deal with string2 in order, you won't have a correct answer.
I'd suggest iterating over all the substrings of length len(s1) in s2. This is a straightforward for loop. Once you have the substrings, you can compare them (sorted vs sorted) with s1 to decide if there is any rearrangement of s1 that yields a contiguous substring of s2.
Viz:
s1 = "jks"
s2 = "aksjd"
print('s1=',s1, ' s2=', s2)
for offset in range(len(s2) - len(s1) + 1):
ss2 = s2[offset:offset+len(s1)]
if sorted(ss2) == sorted(s1):
print('{} is an anagram of {} at offset {} in {}'.format(ss2, s1, offset, s2))
Beginner here, and haven't found an answer for this question though some are similar.
If I have two strings:
s1 = 'abcdefghijk'
s2 = 'abcdefghi'
How do I get 'jk' as an output? 'abcdefghi' must first match, and then I get the difference on the end.
The next after this (which I may be able to figure out if I get the first question answered) is what if s2 = 'cdefghi' and I still want output to be only 'jk' not 'ab' and 'jk'.
You can find the first index of s2 in s1 with find(), i.e.:
def after(s1, s2):
index = s1.find(s2)
# return None if s2 is not part of s1
# or if there are no characters behind s2 in s1
if index != -1 and index + len(s2) < len(s1):
return s1[index + len(s2):]
else:
return None
s1 = "abcdefghijk"
s2 = "cdefghij"
print(after(s1, s2))
For the first case, case s1 = 'abcdefghijk' s2 = 'abcdefghi' , the below would work too.
>>> set(s1) - set(s2)
{'j', 'k'}
>>> ''.join( set(s1) - set(s2))
'jk'
So basically set logic can be applied on strings to extract overlapping and non-overlapping parts of the mentioned strings.
For more info ... https://docs.python.org/2/library/sets.html
But for the 2nd case , #user3760780 suggestion seems to be the best fit.
You can use the string method index to find the start of the substring, then add the length of the substring to get where you want to start taking your extra difference from.
base = 'abcdefghijk'
sub = 'abcdefghi'
def extra(base, sub):
start = base.index(sub)
end = start + len(sub)
return base[end:]
extra(base, sub)
A ValueError will be thrown here if sub is not a substring, and you can choose to do what you wish in that case.
Edit: based on your comment on your question, to return nothing - I'm guessing you mean maybe an empty string - do:
def diff(base, sub):
try:
start = base.index(sub)
end = start + len(sub)
return base[end:]
except ValueError:
return ''
Whether you use find or index here probably depends on what you're actually wanting to use this for.
Most of the questions I've found are biased on the fact they're looking for letters in their numbers, whereas I'm looking for numbers in what I'd like to be a numberless string.
I need to enter a string and check to see if it contains any numbers and if it does reject it.
The function isdigit() only returns True if ALL of the characters are numbers. I just want to see if the user has entered a number so a sentence like "I own 1 dog" or something.
Any ideas?
You can use any function, with the str.isdigit function, like this
def has_numbers(inputString):
return any(char.isdigit() for char in inputString)
has_numbers("I own 1 dog")
# True
has_numbers("I own no dog")
# False
Alternatively you can use a Regular Expression, like this
import re
def has_numbers(inputString):
return bool(re.search(r'\d', inputString))
has_numbers("I own 1 dog")
# True
has_numbers("I own no dog")
# False
You can use a combination of any and str.isdigit:
def num_there(s):
return any(i.isdigit() for i in s)
The function will return True if a digit exists in the string, otherwise False.
Demo:
>>> king = 'I shall have 3 cakes'
>>> num_there(king)
True
>>> servant = 'I do not have any cakes'
>>> num_there(servant)
False
Use the Python method str.isalpha(). This function returns True if all characters in the string are alphabetic and there is at least one character; returns False otherwise.
Python Docs: https://docs.python.org/3/library/stdtypes.html#str.isalpha
https://docs.python.org/2/library/re.html
You should better use regular expression. It's much faster.
import re
def f1(string):
return any(i.isdigit() for i in string)
def f2(string):
return re.search('\d', string)
# if you compile the regex string first, it's even faster
RE_D = re.compile('\d')
def f3(string):
return RE_D.search(string)
# Output from iPython
# In [18]: %timeit f1('assdfgag123')
# 1000000 loops, best of 3: 1.18 µs per loop
# In [19]: %timeit f2('assdfgag123')
# 1000000 loops, best of 3: 923 ns per loop
# In [20]: %timeit f3('assdfgag123')
# 1000000 loops, best of 3: 384 ns per loop
You could apply the function isdigit() on every character in the String. Or you could use regular expressions.
Also I found How do I find one number in a string in Python? with very suitable ways to return numbers. The solution below is from the answer in that question.
number = re.search(r'\d+', yourString).group()
Alternatively:
number = filter(str.isdigit, yourString)
For further Information take a look at the regex docu: http://docs.python.org/2/library/re.html
Edit: This Returns the actual numbers, not a boolean value, so the answers above are more correct for your case
The first method will return the first digit and subsequent consecutive digits. Thus 1.56 will be returned as 1. 10,000 will be returned as 10. 0207-100-1000 will be returned as 0207.
The second method does not work.
To extract all digits, dots and commas, and not lose non-consecutive digits, use:
re.sub('[^\d.,]' , '', yourString)
I'm surprised that no-one mentionned this combination of any and map:
def contains_digit(s):
isdigit = str.isdigit
return any(map(isdigit,s))
in python 3 it's probably the fastest there (except maybe for regexes) is because it doesn't contain any loop (and aliasing the function avoids looking it up in str).
Don't use that in python 2 as map returns a list, which breaks any short-circuiting
You can accomplish this as follows:
if a_string.isdigit():
do_this()
else:
do_that()
https://docs.python.org/2/library/stdtypes.html#str.isdigit
Using .isdigit() also means not having to resort to exception handling (try/except) in cases where you need to use list comprehension (try/except is not possible inside a list comprehension).
You can use NLTK method for it.
This will find both '1' and 'One' in the text:
import nltk
def existence_of_numeric_data(text):
text=nltk.word_tokenize(text)
pos = nltk.pos_tag(text)
count = 0
for i in range(len(pos)):
word , pos_tag = pos[i]
if pos_tag == 'CD':
return True
return False
existence_of_numeric_data('We are going out. Just five you and me.')
You can use range with count to check how many times a number appears in the string by checking it against the range:
def count_digit(a):
sum = 0
for i in range(10):
sum += a.count(str(i))
return sum
ans = count_digit("apple3rh5")
print(ans)
#This print 2
import string
import random
n = 10
p = ''
while (string.ascii_uppercase not in p) and (string.ascii_lowercase not in p) and (string.digits not in p):
for _ in range(n):
state = random.randint(0, 2)
if state == 0:
p = p + chr(random.randint(97, 122))
elif state == 1:
p = p + chr(random.randint(65, 90))
else:
p = p + str(random.randint(0, 9))
break
print(p)
This code generates a sequence with size n which at least contain an uppercase, lowercase, and a digit. By using the while loop, we have guaranteed this event.
any and ord can be combined to serve the purpose as shown below.
>>> def hasDigits(s):
... return any( 48 <= ord(char) <= 57 for char in s)
...
>>> hasDigits('as1')
True
>>> hasDigits('as')
False
>>> hasDigits('as9')
True
>>> hasDigits('as_')
False
>>> hasDigits('1as')
True
>>>
A couple of points about this implementation.
any is better because it works like short circuit expression in C Language and will return result as soon as it can be determined i.e. in case of string 'a1bbbbbbc' 'b's and 'c's won't even be compared.
ord is better because it provides more flexibility like check numbers only between '0' and '5' or any other range. For example if you were to write a validator for Hexadecimal representation of numbers you would want string to have alphabets in the range 'A' to 'F' only.
What about this one?
import string
def containsNumber(line):
res = False
try:
for val in line.split():
if (float(val.strip(string.punctuation))):
res = True
break
except ValueError:
pass
return res
containsNumber('234.12 a22') # returns True
containsNumber('234.12L a22') # returns False
containsNumber('234.12, a22') # returns True
I'll make the #zyxue answer a bit more explicit:
RE_D = re.compile('\d')
def has_digits(string):
res = RE_D.search(string)
return res is not None
has_digits('asdf1')
Out: True
has_digits('asdf')
Out: False
which is the solution with the fastest benchmark from the solutions that #zyxue proposed on the answer.
Also, you could use regex findall. It's a more general solution since it adds more control over the length of the number. It could be helpful in cases where you require a number with minimal length.
s = '67389kjsdk'
contains_digit = len(re.findall('\d+', s)) > 0
Simpler way to solve is as
s = '1dfss3sw235fsf7s'
count = 0
temp = list(s)
for item in temp:
if(item.isdigit()):
count = count + 1
else:
pass
print count
alp_num = [x for x in string.split() if x.isalnum() and re.search(r'\d',x) and
re.search(r'[a-z]',x)]
print(alp_num)
This returns all the string that has both alphabets and numbers in it. isalpha() returns the string with all digits or all characters.
This too will work.
if any(i.isdigit() for i in s):
print("True")
You can also use set.intersection
It is quite fast, better than regex for small strings.
def contains_number(string):
return True if set(string).intersection('0123456789') else False
An iterator approach. It consumes all characters unless a digit is met. The second argument of next fix the default value to return when the iterator is "empty". In this case it set to False but also '' works since it is casted to a boolean value in the if.
def has_digit(string):
str_iter = iter(string)
while True:
char = next(str_iter, False)
# check if iterator is empty
if char:
if char.isdigit():
return True
else:
return False
or by looking only at the 1st term of a generator comprehension
def has_digit(string):
return next((True for char in string if char.isdigit()), False)
I'm surprised nobody has used the python operator in. Using this would work as follows:
foo = '1dfss3sw235fsf7s'
bar = 'lorem ipsum sit dolor amet'
def contains_number(string):
for i in range(10):
if str(i) in list(string):
return True
return False
print(contains_number(foo)) #True
print(contains_number(bar)) #False
Or we could use the function isdigit():
foo = '1dfss3sw235fsf7s'
bar = 'lorem ipsum sit dolor amet'
def contains_number(string):
for i in list(string):
if i.isdigit():
return True
return False
print(contains_number(foo)) #True
print(contains_number(bar)) #False
These functions basically just convert s into a list, and check whether the list contains a digit. If it does, it returns True, if not, it returns False.