Find if an array contains a string with one mismatch - python

I want say there is a string in the list with only one mismatch
def check(list, s):
n = len(list)
# If the array is empty
if (n == 0):
return False
for i in range(0, n, 1):
# If sizes are same
if (len(list[i]) != len(s)):
continue
diff = False
for j in range(0, len(list[i]), 1):
if (list[i][j] != s[j]):
# If first mismatch
if (diff == False):
diff = True
# Second mismatch
else:
diff = False
break
if (diff):
return True
return False
This code is okay but it works slowly. How can I make it faster using a dictionary?

You can use zip() to pair up each character of two strings and sum() to count the number of mismatches. The any() function will allow you to check for a match over every item in the list:
def check(sList,string):
return any(len(string)==len(s) and sum(a!=b for a,b in zip(s,string))==1
for s in sList)
check(["abcd","cdef","efgh"],"xfgh") # True
check(["abcd","cdef","efgh"],"abdc") # False
This translates to the following basic for-loop:
def check(sList,string):
for s in sList:
if len(string) !=len(s): continue
if sum(a!=b for a,b in zip(s,string)) == 1:
return True
return False
[EDIT] to make this go a bit faster, the string comparisons can use zip() as an iterator so that the comparison doesn't need to go to the end of every string:
def check(sList,string):
for s in sList:
if len(string) !=len(s): continue
mismatch = (a!=b for a,b in zip(s,string)) # non-matching iterator
if not any(mismatch): continue # at least one
if any(mismatch): continue # no more after that
return True
return False

Related

How to solve the duplicate integer issue in an array for the isValidSubsequence(array, sequence) question

prompt for isValidSubsequence
def isValidSubsequence(array, sequence):
index = -1
# issue is with initialising the index variable
if len(sequence)<=len(array):
for i in range(len(sequence)):
# i refers to the i in the sequence
if sequence[i] in array:
# causing a problem for repetitions such as array = [1,1,1,1,1] and subsequence = [1,1,1]
# array.index(sequence[i]) would call the first 1 instead of the second 1
if array.index(sequence[i]) > index:
index = array.index(sequence[i])
else:
return False
else:
return False
return True
else:
return False
How to solve this repeating 1s issue using my code?
The reason array.index(sequence[i]) calls the first 1 is because sequence[i] is 1 in your case, and that is the same as if you called array.index(1). The index function searches your array for 1 and as soon as it finds it, it returns its index, so it always finds the first 1 first and then stops looking and returns its index.
Try something like this, this should work:
def isValidSubsequence(array, sequence):
lastIndex = -1
if not len(array) >= len(sequence):
return False
for i in range(len(sequence)):
containsInt = False
for j in range(lastIndex + 1, len(array)):
if array[j] == sequence[i]:
containsInt = True
lastIndex = j
break
if not containsInt:
return False
return True
You are using lists, not arrays. list.index(x[, start[, end]]) can take positionals from where to where search - you could rewrite your algo to remember the last found position for each value and look for new values from that position.
BUT: this is irrelevant, you only need to concern yourself with how many of each element are in original and sequence and if one has at most equal of them...
The correct way to solve this ist to count the occurences of elements in the original, subtract the amount of occurences in the `sequence`` and evaluate your numbers:
If sequence is empty, contains elements not in original or contains more elements then inside original it can not be a subsequence:
from collections import Counter
def isValidSubsequence(array, sequence):
# check empty sequence
if not sequence:
return False
original = Counter(array)
# check any in seq that is not in original
if any(i not in original for i in sequence):
return False
# remove number of occurences in sequence from originals
original.subtract(sequence)
# check if too many of them in sequence
if any(v < 0 for v in original.values()):
return False
return True
Testcode:
source = [1,2,3,4]
test_cases = [[1], [2,4], [1,2,4] ]
neg_test_case = [[2,5], [9], []]
for tc in test_cases:
print(isValidSubsequence(source,tc), "should be True")
for ntc in neg_test_case:
print(isValidSubsequence(source,ntc), "should be False")
Output:
True should be True
True should be True
True should be True
False should be False
False should be False
False should be False
If you are not allowed to import, count yourself (expand this yourself):
# not efficient
counted = {d:original.count(d) for d in frozenset(original)}
# remove numbers
for d in sequence:
counted.setdefault(d,0)
counted[d] -= 1
This would be another way to answer the question.
def isValidSubsequence(array,sequence):
if len(array) >= len(sequence):
iter_sequence = iter(sequence)
last = next(iter_sequence)
total = 0
for i in array:
if i + total == last:
total += i
try:
last += next(iter_sequence)
except StopIteration:
return True
break
return False

Recursive Elimination of Character Groups in a String in Python

Given a string with a series of Rs and Gs, search for a way to eliminate them by groups. You can ONLY eliminate if characters are grouped (at least 2).
Example 1: RGRRRGGGRR
v v v
RGRRRGGGRR = RGGGGRR = RRR = {empty}, thus there is a possible solution.
Example 2: GGGRGRGRG
v
GGGRGRGRG = RGRGRG, can no longer find groups, thus there is NO possible solution.
I made an initial recursive function but it ends with no solution even though there is a possible solution.
def fn(string, start):
l = len(string)
if l-start > 0:
counter, isPop = 0, False
if string[start] == 'R':
while counter + start < l and string[counter + start] == 'R': counter+=1
if counter > 1: # If there is the same letter adjacent to the current letter being checked, it will pop this group.
for i in range(counter):
string.pop(start)
isPop = True
if not isPop and start+counter == l: return False # Returns false if previous action did not pop and
else: return fn(string, 0 if isPop else start+counter) # Letter being checked is the last element (it means
# that there are still elements in the string that can
else: # no longer be grouped and eliminated.
while counter + start < l and string[counter + start] == 'G': counter+=1
if counter > 1:
for i in range(counter):
string.pop(start)
isPop = True
if not isPop and start+counter == len(string): return False
else: return fn(string, 0 if isPop else start+counter)
if string: return False
else: return True
ways = []
string = input()
for j in range(len(string)): # Iterates through every position in the list and calls the fn just to see if it is possible.
if True in ways: # If there is a possible solution, it breaks the loop and prints "possible"
break
ways.append(fn(list(string), j))
if True in ways: print("Possible")
else: print("Impossible")
Cases that my algo doesn't work (returns false but there is actually a way):
1. GGRRGRRRRGGGGRGRGG
v v v v v v
GGRRGRRRRGGGGRGRGG = GGRRGRRRRRGRGG = GGRRGGRGG = GGRRRGG = GGRRRGG = GGGG = {empty}
2. GRRRGRRRGRRRGRRRGR
v v v v v v
GRRRGRRRGRRRGRRRGR = GRRRGRRRGGRRRGR = GRRRGRRRGGGR = GRRRGRRRR = GGRRRR = RRRR = {empty}
You can use recursion with a generator. Additionally, applying itertools.groupby to produce groupings up front leads to a shorter solution:
from itertools import groupby as gb
def get_groups(s, chain=[]):
if not s:
yield chain+[s]
else:
r = [list(b) for _, b in gb(s)]
for i, a in enumerate(r):
if len(a) > 1:
t = ''.join(map(''.join, [*r[:i], *([] if i >= len(r) else r[i+1:])]))
yield from get_groups(t, chain+[s])
cases = ['RGRRRGGGRR', 'GGGRGRGRG', 'GGRRGRRRRGGGGRGRGG', 'GRRRGRRRGRRRGRRRGR']
for case in cases:
print(f'{case}: {any(get_groups(case))}')
Output:
RGRRRGGGRR: True
GGGRGRGRG: False
GGRRGRRRRGGGGRGRGG: True
GRRRGRRRGRRRGRRRGR: True
get_groups produces an empty list [] if there is no possible path whereby the input is completed reduced to an empty string by removing groupings, and a listing of all the possible paths to an empty string.
I believe your primary issue with respect to debugging this code is your coding style.
Besides being messy, you have ambiguous return situations; your 'R' and 'G' parallel blocks aren't identical; you fail to update the array length variable after popping the array; you appear to start the recursion at the wrong index.
The best I could come up with cleaning up your code is the following that passes one more case but still fails one:
def fn(characters, start):
length = len(characters)
if length - start > 0:
for character in ['R', 'G']:
counter = 0
isPop = False
if characters[start] == character:
while start+counter < length and characters[start+counter] == character:
counter += 1
if counter > 1:
# If there is the same letter adjacent to the current
# letter being checked, it will pop this group.
for _ in range(counter):
characters.pop(start)
isPop = True
length -= 1 # we've shortened the array
if not isPop and start+counter == length:
# Returns false if previous action did not pop and
# letter being checked is the last element (it means
# that there are still elements in characters that can
# no longer be grouped and eliminated.
return False
return fn(characters, start if isPop else start+counter)
if characters:
return False
return True
My own solution is akin to that of #Ajax1234 as far as groupby goes:
from itertools import groupby
def eliminate_groups(string):
if not string: # base case of recursion
return True
# "RGRRRGGGRR" -> ['R', 'G', 'RRR', 'GGG', 'RR']
groups = [''.join(g) for _, g in groupby(string)]
for index, group in enumerate(groups):
if len(group) < 2:
continue
reduced = groups[:index] + groups[index+1:] # a deficient copy
status = eliminate_groups(''.join(reduced))
if status: # one of the variants succeeded!
return status
return False # all of the variants failed!
if __name__ == '__main__':
strings = ["RGRRRGGGRR", "GGGRGRGRG", "GGRRGRRRRGGGGRGRGG", "GRRRGRRRGRRRGRRRGR"]
for string in strings:
print(string, eliminate_groups(string))
OUTPUT
> python3 test.py
RGRRRGGGRR True
GGGRGRGRG False
GGRRGRRRRGGGGRGRGG True
GRRRGRRRGRRRGRRRGR True
>

For loops that include range in python3

I need to define a function receives a string that is made of numbers and checks the following requirement:
the string isnt empty
the first character is in range (1,10)
every character besides the first is in range (0,10)
if the string answers all the requirements return True, else return False.
What ive tried is if in for loops and vice versa.
I've also tried moving the return function in the following code to different indentations which didn't help
def is_positive_int(st):
n = len(st)
if n > 0:
if st[0]>= 0 and st[0]<= 9:
for i in range(1,n):
if st[i]>= 0 and st[i]<= 9
return True
I've also tried:
def is_positive_int(st):
n = len(st)
for n > 0:
if st[0] >= 1 and st[0]<=9:
for i in range (1,n):
if st[i]>=1 and st[i]<= 9
return True
else:
return False
which returns: invalid syntax for n > 0 (I'm not sure why)
For example:
print(is_positive_int("123"))
should return True
Where as
print(is_positive_int("123.0"))
should return False
And
print(is_positive_int("0"))
should return False
You can also just iterate over the string:
for character in st:
# your conditionals
Then use character in place of st[i]
But also note, that with the conditionals you can not compare a string with a number. So what you should do is either make a set of the digits or use the isdigit() method for strings.
Taking it all together your function should be something like this:
def is_positive_int(st):
if not (st and st[0] != '0'):
return False
for character in st:
if not character.isdigit():
return False
return True
I can suggest 2 alternative approaches:
Option 1: iterate directly over the string and compare each char to allowed chars for each position (do not compare chars to numbers).
import string
def func(input_str):
if len(input_str) == 0:
return False
for i, c in enumerate(input_str):
if i == 0:
allowed_chars = string.digits[1:] # exclude '0'
else:
allowed_chars = string.digits
if c not in allowed_chars:
return False
return True
Option 2: use regular expressions; I know that they aren't always a solution, but in this case they allow for a really short code solution.
import re
def func(input_str):
if re.match(r'^[1-9][0-9]*$', input_str):
return True
return False
Does this help?
Try this
If you input something that isnt a string that can convert to int, it will run the except condition. len(input)>1 just confirms that there is a second digit
def is_positive_int(input):
try:
if int(input[0]) in range(1,10) and int(input) and len(input)>1:
return True
else:
return False
except:
print('invalid input')
Use range(len(iterable)) to iterate over the indexes of an iterable:
def is_positive_int(string):
positive_ints = {1, 2, 3, 4, 5, 6, 7, 8, 9}
if len(string) == 0:
return False
if int(string[0]) not in positive_ints:
return False
for i in range(1, len(string)):
if int(string[i]) not in (positive_ints | {0}):
return False
return True
Note the use of sets to define accepted numbers, since it's an efficient data structure for this purpose, and could also be constructed dynamically with positive_ints = set(range(1,10)).
The int() calls are there because the elements of the string (string[i]) will be strings, and would always compare to false since 1 == "1" is False.
Lastly, the cascading style of returns is often preferred as it skips parts of the function that won't be relevant and only returns True if nothing else stops it until the end.
Instead of operating on indexes, you can iterate on the characters of the string themselves:
def is_positive_int(string):
positive_ints = set(range(1, 10))
if len(string) == 0:
return False
if int(string[0]) not in positive_ints:
return False
for character in string:
if int(character) not in (positive_ints | {0}):
return False
return True
Note that both of these will raise an exception if a character in the string cannot be converted to int. This can be prevented by converting the numbers to string instead:
def is_positive_int(string):
positive_ints = set(str(x) for x in range(1, 10))
if len(string) == 0:
return False
if string[0] not in positive_ints:
return False
for i in range(1, len(string)):
if string[i] not in (positive_ints | {"0"}):
return False
return True
You need to convert the entries in your string to int, otherwise your conditional statements will throw an error:
your code:
def is_positive_int(st):
n = len(st)
if n > 0:
if (st[0]>= 0) and (st[0]<= 9):
for i in range(1,n):
if st[i]>= 0 and st[i]<= 9:
return True
Should throw an error. At least it does when I run it on '123'
TypeError: '>=' not supported between instances of 'str' and 'int'
So, you could change to:
def is_positive_int(st):
n = len(st)
if n > 0:
if (int(st[0])>= 0) and (int(st[0])<= 9):
for i in range(1,n):
if int(st[i])>= 0 and int(st[i])<= 9:
return True
Since nowhere in this function it is asserted if there are non-digit characters, it cannot return False when encountering those. Contrary, it will throw an error, when it tries the conversion. So you could do:
def is_positive_int(st):
entries = list(st) # create a list of all items in the string
if entries: # assert this list has length > 0
output = True # initialise output
for e in entries:
output *= e.isdigit() # update outputs : a single False will convert output to 0.
return bool(output) # convert output to boolean
else: # if the string is empty, return False.
return False
if you have to check only for positive integers you can use .is.isdigit
def is_positive_int(st):
return st.isdigit() and int(st) > 0
>>>is_positive_int('123')
True
>>>is_positive_int('123.0')
False
>>>is_positive_int('0')
False
>>>is_positive_int('#$%')
False
>>>is_positive_int('-123')
False
>>>is_positive_int('')
False
You can use the str.isdigit function to check if a string is a digit between 0 and 9, and we can utilize it here as follows.
def is_positive_int(st):
#Get the length of the string
n = len(st)
#Flag to capture result
result = True
#If the string is empty, result is False
if n == 0:
result = False
#If the string has only one character, if the character is not 0, result is False
elif n == 1:
if st[0] == '0':
result = False
else:
#Otherwise iterate through all characters in the string
for i in range(1, n):
#If we find a character which is not a digit, result is False
if not st[i].isdigit():
result = False
break
#Return the final result
return result
The outputs for the function above then will be
print(is_positive_int(""))
#False
print(is_positive_int("0"))
#False
print(is_positive_int("1"))
#True
print((is_positive_int("123.0")))
#False
print((is_positive_int("123")))
#True
Using the builtin function all() and a list comprehension, just to add to the many options.
def test_num(num):
# check for empty string
if len(num) == 0:
return False
# check all numbers are digits,
# if so, is the first digit > 0
return all(i.isdigit() for i in num) and int(num[0]) > 0
if __name__ == '__main__':
for num in ['', '0', '1', '123.0', '123', '+123', '-123']:
print(f'\'{num}\' is positive: {test_num(num)}')
# '' is positive: False
# '0' is positive: False
# '1' is positive: True
# '123.0' is positive: False
# '123' is positive: True
# '+123' is positive: False
# '-123' is positive: False

Find the next iteration in loop python

I am trying to find out if given a string if 2 target characters follow one another. So essentially, I am trying to find if a character and its neighbor are target characters. How should I go about this? The following is what I have tried so far.
target_list=["t","a","r","g","e","t"]
for char in some_string:
if (char and some_string[some_string.index(char)+1]) in target_list:
print ("correct")
else:
print ("incorrect")
Expected output:
if some_string="heytr" == "correct"
if some_string="hyt" == "incorrect"
if some_string="heyt" == "incorrect"
Just go through the indices and process every pair of characters:
for i in range(len(some_string) - 1):
if some_string[i] in target_list and some_string[i+1] in target_list:
print ("correct")
break
if i == len(some_string) - 1:
print ("incorrect")
You can alternatively create a mapping and look for adjacent true positives:
m = [(char in target_list) for char in some_string]
for i in range(len(m) - 1):
if m[i] and m[i+1]:
print ("correct")
break
if i == len(m) - 1:
print ("incorrect")
Just use map:
target_list=['t','a','r','g','e']
def toDict(list):
mp = {}
for c in list:
mp[c] = True
return mp
d = toDict(target_list)
print("dict:" , d)
def check(string, mp):
count = 0
for c in string:
if(mp.get(c,False)):
count = count+1
if(count > 1):
return True
else:
count = 0
return False
print("check:" , check("heytr", d))
print("check:" , check("hyt", d))
print("check:" , check("heyt", d))
from itertools import tee
def rolling_window(iterable, n=2):
iterators = tee(iterable, n)
for i, iterator in enumerate(iterators):
for skip in range(i):
next(iterator, None)
return zip(*iterators)
def match(some_string, target, n=2):
target = set(target)
return any(target.issuperset(s) for s in rolling_window(some_string, n=n))
A regular expression solution.
import re
target_chars='target'
p = re.compile('[%s]{2}' % re.escape(target_chars))
m = p.search(some_string)
if m:
print('correct')
else:
print('incorrect')
You can also use any() and zip() for this:
target_list=["t","a","r","g","e","t"]
target_list = set(target_list)
some_strings = ["heytr", "hyt", "heyt"]
def match_strings(string, target_list):
return any(x in target_list and y in target_list for x, y in zip(string, string[1:]))
for some_string in some_strings:
if match_strings(some_string, target_list):
print("correct")
else:
print("incorrect")
Which Outputs:
correct
incorrect
incorrect
Logic of above code:
Converts target_list to a set, since set lookup is constant time. If you keep it as a list, then lookup time is linear.
Uses zip() to create pairs of the current element and the next element, and checks if both of these elements exist in target_list. Then checks if any() of the pairs exist, which returns True if any of the pairs exist, and False if none exist.
Then loop over all the strings in some_strings, and check the above for each one.
def judge(some_string):
for index in range(0,len(some_string)-1):
if some_string[index] in target_list and some_string[index+1] in target_list:
print("correct")
break
else:
print("incorrect")
judge("heytr") --correct
judge("hyt") -- incorrect
judge("heyt") --incorrect
For loop iterates on all character of the string checks if i and i+1 is present in target.if yes prints out correct. after all iterations of loop is over if no two character are found in target it comes to the else part of for loop and prints out incorrect.

How to compare a return value of a recursive function with an integer (actual parameter of function)

I need to compare the return value of a recursive function with an integer.
Doing recursively the sum of all elements of a list , I have to compare the final sum with an integer "n" giving back TRUE if sum == n , FALSE if sum != n . In addiction to the function have to return FALSE if i'm giving an empty list .
Here I report the code to clarify the situation :)
def function(list_of_numbers,int):
if not list:
return false # I have to return false if list is empty.
if len(l) > 0:
return l[0] + function(list_of_numbers[1:],int) # recursive sum of element
# and here i'm stuck !
when not l we either got passed an empty list or have reached our base case so compare n to our test number, if you want an empty list to return True change test=-1 to test=0 :
def function(l, n=0, test=-1):
if not l:
return n == test
else:
n += l[0]
return function(l[1:], n, test)
In [2]: function([1,2,3],test=6)
Out[2]: True
In [3]: function([1,2,3],test=5)
Out[3]: False
In [4]: function([1,2,3])
Out[4]: False
In [5]: function([])
Out[5]: False
If you want an empty list to return False you can check how many times the function has been called and either compare n to test or return False :
def function(l, n=0, test=0, calls=0):
if not l:
return n == test if calls > 0 else False
else:
n += l[0]
calls += 1
return function(l[1:], n, test,calls)
If you just want to pass a single argument the number to test against:
def function(l, test, n=0, calls=0):
if not l and calls == 0: # changed for jython
return False
if not l:
return n == test
else:
n += l[0]
calls += 1
return function(l[1:],test,n, calls)

Categories