Related
So, I would like to convert my string input
'f(g,h(a,b),a,b(g,h))'
into the following list
['f',['g','h',['a','b'],'a','b',['g','h']]]
Essentially, I would like to replace all '(' into [ and all ')' into ].
I have unsuccessfully tried to do this recursively. I thought I would iterate through all the variables through my word and then when I hit a '(' I would create a new list and start extending the values into that newest list. If I hit a ')', I would stop extending the values into the newest list and append the newest list to the closest outer list. But I am very new to recursion, so I am struggling to think of how to do it
word='f(a,f(a))'
empty=[]
def newlist(word):
listy=[]
for i, letter in enumerate(word):
if letter=='(':
return newlist([word[i+1:]])
if letter==')':
listy.append(newlist)
else:
listy.extend(letter)
return empty.append(listy)
Assuming your input is something like this:
a = 'f,(g,h,(a,b),a,b,(g,h))'
We start by splitting it into primitive parts ("tokens"). Since your tokens are always a single symbol, this is rather easy:
tokens = list(a)
Now we need two functions to work with the list of tokens: next_token tells us which token we're about to process and pop_token marks a token as processed and removes it from the list:
def next_token():
return tokens[0] if tokens else None
def pop_token():
tokens.pop(0)
Your input consist of "items", separated by a comma. Schematically, it can be expressed as
items = item ( ',' item )*
In the python code, we first read one item and then keep reading further items while the next token is a comma:
def items():
result = [item()]
while next_token() == ',':
pop_token()
result.append(item())
return result
An "item" is either a sublist in parentheses or a letter:
def item():
return sublist() or letter()
To read a sublist, we check if the token is a '(', the use items above the read the content and finally check for the ')' and panic if it is not there:
def sublist():
if next_token() == '(':
pop_token()
result = items()
if next_token() == ')':
pop_token()
return result
raise SyntaxError()
letter simply returns the next token. You might want to add some checks here to make sure it's indeed a letter:
def letter():
result = next_token()
pop_token()
return result
You can organize the above code like this: have one function parse that accepts a string and returns a list and put all functions above inside this function:
def parse(input_string):
def items():
...
def sublist():
...
...etc
tokens = list(input_string)
return items()
Quite an interesting question, and one I originally misinterpreted. But now this solution works accordingly. Note that I have used list concatenation + operator for this solution (which you usually want to avoid) so feel free to improve upon it however you see fit.
Good luck, and I hope this helps!
# set some global values, I prefer to keep it
# as a set incase you need to add functionality
# eg if you also want {{a},b} or [ab<c>ed] to work
OPEN_PARENTHESIS = set(["("])
CLOSE_PARENTHESIS = set([")"])
SPACER = set([","])
def recursive_solution(input_str, index):
# base case A: when index exceeds or equals len(input_str)
if index >= len(input_str):
return [], index
char = input_str[index]
# base case B: when we reach a closed parenthesis stop this level of recursive depth
if char in CLOSE_PARENTHESIS:
return [], index
# do the next recursion, return it's value and the index it stops at
recur_val, recur_stop_i = recursive_solution(input_str, index + 1)
# with an open parenthesis, we want to continue the recursion after it's associated
# closed parenthesis. and also the recur_val should be within a new dimension of the list
if char in OPEN_PARENTHESIS:
continued_recur_val, continued_recur_stop_i = recursive_solution(input_str, recur_stop_i + 1)
return [recur_val] + continued_recur_val, continued_recur_stop_i
# for spacers eg "," we just ignore it
if char in SPACER:
return recur_val, recur_stop_i
# and finally with normal characters, we just extent it
return [char] + recur_val, recur_stop_i
You can get the expected answer using the following code but it's still in string format and not a list.
import re
a='(f(g,h(a,b),a,b(g,h))'
ans=[]
sub=''
def rec(i,sub):
if i>=len(a):
return sub
if a[i]=='(':
if i==0:
sub=rec(i+1,sub+'[')
else:
sub=rec(i+1,sub+',[')
elif a[i]==')':
sub=rec(i+1,sub+']')
else:
sub=rec(i+1,sub+a[i])
return sub
b=rec(0,'')
print(b)
b=re.sub(r"([a-z]+)", r"'\1'", b)
print(b,type(b))
Output
[f,[g,h,[a,b],a,b,[g,h]]
['f',['g','h',['a','b'],'a','b',['g','h']] <class 'str'>
Given a string, find the first non-repeating character in it and return its index. If it doesn't exist, return -1. Input string already all lowercase.
Why does my code not work?
str1 = input("give me a string: ")
def unique(x):
stack = []
if x is None:
return (-1)
i = 0
while i < len(x):
stack = stack.append(x[i])
if x[i] in stack:
return(i)
else:
i += 1
unique(str1)
str1 = input("give me a string: ")
def unique(x):
for i in x:
if x.count(i) == 1:
return x.index(i)
else:
return -1
print(unique(str1))
This will work
Explanation
Instead of using the list stack use the count() function of the string. The function unique(x) will return the index of that first element whose count is 1 in the str1 string.
You need to know what your code is doing to figure out why it doesn't work, let's breakthrough it step by step.
you create a empty list stack for later use, that's fine.
if x is None is a strange way to check if a string is given, and it doesn't work because even a empty string "" is not equal to None. is is used to check if both sides are the same object, and == is a better operator to check if values of both sides are the same. Therefore, if x == "" is better, but if not x is even better to check if something is empty.
using variable i and while loop to iterate the string is fine.
append() change the list in-place and return None, so stack = stack.append(x[i]) is assigning None to stack.
in stack is going to raise TypeError as NoneType is not iterable. If we change the last line to stack.append(x[i]), it now works, as x[0] is already appended to stack, if x[0] in stack must be True and return 0 for your result.
That's what your code is doing, you just append the first character and return the first index. You need to go through the whole string to know if a character is unique.
Although Rishabh's answer is cleaner, I provide a way to doing it using lists to save seen and repeated characters, then read the string again to find the index of unique character.
x = input("give me a string: ")
def unique(x):
seen = []
repeated = []
for char in x:
if char in seen:
repeated.append(char)
else:
seen.append(char)
for idx, char in enumerate(x):
if char not in repeated:
return idx
return -1
print(unique(x))
I'm trying to write a function to return the longest common prefix from a series of strings. Using a debugger, saw that my function reaches the longest common prefix correctly, but then when it reaches the statement to return, it begins reverting to earlier stages of the algorithm.
For test case strs = ["flower","flow","flight"]
The output variable holds the following values:-
f > fl > f
instead of returning fl.
Any help would be appreciated, because I don't really know how to Google for this one. Thank you.
class Solution(object):
def longestCommonPrefix(self, strs, output = ''):
#return true if all chars in string are the same
def same(s):
return s == len(s) * s[0]
#return new list of strings with first char removed from each string
def slicer(list_, list_2 = []):
for string in list_:
string1 = string[1:]
list_2.append(string1)
return list_2
#return string containing first char from each string
def puller(list_):
s = ''
for string in list_:
s += string[0]
return s
#pull first character from each string
s = puller(strs)
#if they are the same
#add one char to output
#run again on sliced list
if same(s):
output += s[0]
self.longestCommonPrefix(slicer(strs), output)
return output
This can be handled with os.path.commonprefix.
>>> import os
>>> strs = ["flower","flow","flight"]
>>> os.path.commonprefix(strs)
'fl'
It doesn't "revert". longestCommonPrefix potentially calls itself - what you're seeing is simply the call-stack unwinding, and flow of execution is returning to the calling code (the line that invoked the call to longestCommonPrefix from which you are returning).
That being said, there's really no need to implement a recursive solution in the first place. I would suggest something like:
def get_common_prefix(strings):
def get_next_prefix_char():
for chars in zip(*strings):
if len(set(chars)) != 1:
break
yield chars[0]
return "".join(get_next_prefix_char())
print(get_common_prefix(["hello", "hey"]))
You are looking at the behavior...the final result...of recursive calls to your method. However, the recursive calls don't do anything to affect the result of the initial execution of the method. If we look at the few lines that matter at the end of your method:
if same(s):
output += s[0]
self.longestCommonPrefix(slicer(strs), output)
return output
The problem here is that since output is immutable, its value won't be changed by calling longestCommonPrefix recursively. So from the standpoint of the outermost call to longestCommonPrefix, the result it will return is determined only by if same(s) is true or false. If it is true it will return s[0], otherwise it will return ''.
The easiest way to fix this behavior and have your recursive call affect the result of the prior call to the method would be to have its return value become the value of output, like this:
if same(s):
output += s[0]
output = self.longestCommonPrefix(slicer(strs), output)
return output
This is a common code pattern when using recursion. Just this change does seem to give you the result you expect! I haven't analyzed your whole algorithm, so I don't know if it becomes "correct" with just this change.
Can you try this? I
class Solution(object):
def longestCommonPrefix(self, strs, output = ''):
#return true if all chars in string are the same
def same(s):
return s == len(s) * s[0]
#return new list of strings with first char removed from each string
def slicer(list_, list_2 = []):
for string in list_:
string1 = string[1:]
list_2.append(string1)
return list_2
#return string containing first char from each string
def puller(list_):
s = ''
for string in list_:
s += string[0]
return s
#pull first character from each string
s = puller(strs)
# Can you Try this revision?
# I think the problem is that your new version of output is being lost when the fourth called function returns to the third and the third returns to the second, etc...
# You need to calculate a new output value before you call recursively, that is true, but you also need a way to 'store' that output when that recursively called function 'returns'. Right now it disappears, I believe.
if same(s):
output += s[0]
output = self.longestCommonPrefix(slicer(strs), output)
return output
The title is pretty self explanatory. The output only prints out the first letter of the string. I don't know what I'm doing wrong.
string = "hello"
def string_loop(string):
for x in string:
return(x)
print(string_loop(string))
output: h
Problem: Currently, your method is taking in a value string and iterating through it, however as it iterates over the first element, it will return it therefore ending the method call.
def string_loop(string):
for x in string:
return(x)
When you iterate through a string, unlike other collections (lists, dictionaries, ...) python unpacks it into an array of characters so the first element of the array is the first character which is why you only return "h".
Solution:
1) If you wanted to print each character of your string, you have two options:
a) Simply print inside your method with no return value.
def string_loop(string):
for character in string:
print(character)
string_loop("hello")
b) Add your characters to a list and print out the list returned by the method.
def string_loop(string):
my_list = list()
for character in string:
list.append(character)
print(string_loop("hello"))
2) On the other hand, if you were trying to print out every string while iterating through a list of strings, you would could iterate through the individual strings in the list
def string_loop(string_list):
for string in string_list:
print(string)
print(string_loop(["hello","world"]))
I want to check for each position in the string what is the character that appears most often on that position. If there are more of the same frequency, keep the first one. All strings in the list are guaranteed to be of identical length!!!
I tried the following way:
print(max(((letter, strings.count(letter)) for letter in strings), key=lambda x:[1])[0])
But I get: mistul or qagic
And I can not figure out what's wrong with my code.
My list of strings looks like this:
Input: strings = ['mistul', 'aidteh', 'mhfjtr', 'zxcjer']
Output: mister
Explanation: On the first position, m appears twice. Second, i appears twice twice. Third, there is no predominant character, so we chose the first, that is, s. On the fourth position, we have t twice and j twice, but you see first t, so we stay with him, on the fifth position we have e twice and the last r twice.
Another examples:
Input: ['qagic', 'cafbk', 'twggl', 'kaqtc', 'iisih', 'mbpzu', 'pbghn', 'mzsev', 'saqbl', 'myead']
Output: magic
Input: ['sacbkt', 'tnqaex', 'vhcrhl', 'obotnq', 'vevleg', 'rljnlv', 'jdcjrk', 'zuwtee', 'xycbvm', 'szgczt', 'imhepi', 'febybq', 'pqkdfg', 'swwlds', 'ecmrut', 'buwruy', 'icjwet', 'gebgbq', 'djtfzr', 'uenleo']
Expected Output: secret
Some help?
Finally a use case for zip() :-)
If you like cryptic code, it could even be done in one statement:
def solve(strings):
return ''.join([max([(letter, letters.count(letter)) for letter in letters], key=lambda x: x[1])[0] for letters in zip(*strings)])
But I prefer a more readable version:
def solve(strings):
result = ''
# "zip" the strings, so in the first iteration `letters` would be a list
# containing the first letter of each word, the second iteration it would
# be a list of all second letters of each word, and so on...
for letters in zip(*strings):
# Create a list of (letter, count) pairs:
letter_counts = [(letter, letters.count(letter)) for letter in letters]
# Get the first letter with the highest count, and append it to result:
result += max(letter_counts, key=lambda x: x[1])[0]
return result
# Test function with input data from question:
assert solve(['mistul', 'aidteh', 'mhfjtr', 'zxcjer']) == 'mister'
assert solve(['qagic', 'cafbk', 'twggl', 'kaqtc', 'iisih', 'mbpzu', 'pbghn',
'mzsev', 'saqbl', 'myead']) == 'magic'
assert solve(['sacbkt', 'tnqaex', 'vhcrhl', 'obotnq', 'vevleg', 'rljnlv',
'jdcjrk', 'zuwtee', 'xycbvm', 'szgczt', 'imhepi', 'febybq',
'pqkdfg', 'swwlds', 'ecmrut', 'buwruy', 'icjwet', 'gebgbq',
'djtfzr', 'uenleo']) == 'secret'
UPDATE
#dun suggested a smarter way of using the max() function, which makes the one-liner actually quite readable :-)
def solve(strings):
return ''.join([max(letters, key=letters.count) for letters in zip(*strings)])
Using collections.Counter() is a nice strategy here. Here's one way to do it:
from collections import Counter
def most_freq_at_index(strings, idx):
chars = [s[idx] for s in strings]
char_counts = Counter(chars)
return char_counts.most_common(n=1)[0][0]
strings = ['qagic', 'cafbk', 'twggl', 'kaqtc', 'iisih',
'mbpzu', 'pbghn', 'mzsev', 'saqbl', 'myead']
result = ''.join(most_freq_at_index(strings, idx) for idx in range(5))
print(result)
## 'magic'
If you want something more manual without the magic of Python libraries you can do something like this:
def f(strings):
dic = {}
for string in strings:
for i in range(len(string)):
word_dic = dic.get(i, { string[i]: 0 })
word_dic[string[i]] = word_dic.get(string[i], 0) + 1
dic[i] = word_dic
largest_string = max(strings, key = len)
result = ""
for i in range(len(largest_string)):
result += max(dic[i], key = lambda x : dic[i][x])
return result
strings = ['qagic', 'cafbk', 'twggl', 'kaqtc', 'iisih', 'mbpzu', 'pbghn', 'mzsev', 'saqbl', 'myead']
f(strings)
'magic'