I'm trying to make a slightly more advanced palindrome as according to the docstring. However I can't get it to work. Am I going it about the right way? This is what I have so far:
def pal_length(s: str, n: int) -> bool:
'''Return True iff s has a palindrome of length exactly n.
>>> pal_length('abclevel', 5)
True
>>> pal_length('level', 2)
False
'''
if not s:
return True
else:
index = 0
while index < len(s):
if s[index] == s[index+n]:
return pal_length(s[index+1:index+n-1],n-1)
index += 1
return False
I'm trying to not use any import modules etc. Just straight recursion.
Any help is appreciated. Thanks.
I think your indexing is a bit off. Shouldn't it be
index = 0
while index < len(s) - n + 1:
if s[index] == s[index+n-1]:
return pal_length(s[index+1:index+n-1], n-2)
index += 1
return False
Related
I would like to change a function from iteration to recursion.
Here is the function:
def differences(values):
result = []
for i in range(len(values) - 1):
result.append(values [i+1] - values[i])
return result
def palindrome(word):
if len(word) <=1:
return True
if word[0] != word[-1]:
return False
return palindrome(word[1:-1])
In pseudocode:
differences list =
if list has < 2 elements result is empty list
otherwise,
list = first, second, *rest
result is [second - first] + differences([second, *rest])
Implementing this in python, you can elegantly use the new match statement.
Iteration:
palindrome str =
first = 0, last = len(str) - 1
while first < last
if str[first] != str[last] return false
first++, last--
return true
The first function could be made recursive as follows:
def differences2(values, i = 1, res = []):
if i < len(values):
res.append(values[i] - values[i-1])
return differences2(values, i + 1, res)
else:
return res
The second could be made iterative like this:
def palindrome2(word):
pointer = 0
while 2 * pointer + 1 <= len(word):
if word[pointer] != word[-pointer-1]:
return False
pointer += 1
return True
The recursive implementation of this can be very compact:
def palindrome(word, i = 0):
if 2 * i + 1 <= len(word):
return False if word[i] != word[-i-1] else palindrome(word, i = i + 1)
return True
As a side note: in general, while very elegant, I am always a little bit worried in implementing recursive solutions in Python - iterative one are more readable and robust (again, those are my 2 cents).
I tried this to count the number of occurences of the target in a string in a recursive way, but I don't think where len(target) > 1 is a recursive way. I can't really think of other way to do this in a recursive way. I am doing this without using any string methods except indexing and slicing. Please give some help.
target can be a single character or a substring.
For example, if s = 'aaaaabb' and target = 'aa', i want the output to be 4.
def count(s, target):
if s == '':
return 0
if len(target) > len(s):
return 0
if len(target) <= 1:
if s[0] == target:
return 1 + count(s[1:], target)
else:
return 0 + count(s[1:], target)
if len(target) > 1:
count = 0
for x in range(len(s) - len(target) + 1):
if s[x:x+len(target)] == target:
count += 1
return count
Try:
def count_substring(s, target):
if not s:
return 0
return (s[:len(target)] == target) + count_substring(s[1:], target)
print(count_substring('aaaaabb', 'aa')) # 4
The idea is that at each recursion, the function only cares about the left-most substring, to compare with target. You can think of it as a slight modification of your len(target) <= 1 case.
I've been doing a good amount of studying and practicing algorithms, and I ran into one that asked the question (summarizing here) "Given two strings, return True if the strings are one edit away (either by removing, inserting, or replacing a character). Return False if not."
I went about this problem by comparing two strings and counting the amount of letters that are in string1, but not in string2. If there is more than one letter missing, then it will return False.
Here is my Python code:
def oneAway(string1, string2):
string1 = string1.lower()
string2 = string2.lower()
# counts the number of edits required
counter = 0
for i in string1:
if i not in string2:
counter += 1
if counter > 1:
return False
else:
return True
I'd like to hear other people's approaches to this problem, and please point out if I have oversimplified this concept.
Why call .lower() on both strings? Based on the question, oneAway('abc', 'ABC') should be False.
Building off of the other comments, how about this:
def oneAway(s1, s2):
i, j = 0, 0
mistake_occurred = False
while i < len(s1) and j < len(s2):
if s1[i] != s2[j]:
if mistake_occurred:
return False
mistake_occurred = True
i += 1
j += 1
if mistake_occurred and (i != len(s1) or j != len(s2)):
return False
if i < len(s1) - 1 or j < len(s2) - 1:
return False
return True
You have to check each editing action specifically:
def isOneEdit(A,B):
# replacing one char
if len(A) == len(B) and sum(a!=b for a,b in zip(A,B)) == 1:
return True
# inserting one char in A to get B
if len(A) == len(B)-1 and any(A==B[:i]+B[i+1:] for i in range(len(B))):
return True
# removing one char (like inserting one in B to get A)
if len(A) == len(B)+1:
return isOneEdit(B,A)
return False
print(isOneEdit("abc","abd")) # True - replace c with d
print(isOneEdit("abd","abcd")) # True - insert c
print(isOneEdit("abcd","abd")) # True - delete c
print(isOneEdit("abcde","abd")) # False
Alternatively, you can compare the size of the common prefix and suffix of the two strings to the length of the longest one:
def isOneEdit(A,B):
if abs(len(A)-len(B))>1: return False
commonPrefix = next((i for i,(a,b) in enumerate(zip(A,B)) if a!=b),len(A))
commonSuffix = next((i for i,(a,b) in enumerate(zip(reversed(A),reversed(B))) if a!=b),len(B))
editSize = max(len(A),len(B)) - (commonPrefix+commonSuffix)
return editSize <= 1
Hi I am trying to make this program work without using reverse or sort functions in python 3. It is simply supposed to check for palindromes. The problem I am running into is that I get an index out of range everytime I run it. Is there a way to work around this?
def is_palindrome(word):
if len(word) <= 1:
return True
else:
left = 0
right = len(word) - 1
while left < right:
if word[left] == word[right]:
left += 1
right += 1
else:
return False
return True
is_palindrome("mom")
is_palindrome("root")
is_palindrome("racecar")
right += 1 should be right -= 1.
You can write it a bit more succinctly.
>>> def is_palin(w):
... return len(w) <= 1 or (w[0] == w[-1] and is_palin(w[1:-1]))
...
>>> is_palin("xax")
True
>>> is_palin("xaax")
True
>>> is_palin("xaab")
False
I will be happy to get some help.
I have the following problem:
I'm given a list of numbers seq and a target number and I need to write 2 things:
A recursive solution that returns True if there is a sum of a subsequence that equals the target number and False otherwise.
example:
subset_sum([-1,1,5,4],0) # True
subset_sum([-1,1,5,4],-3) # False
Secondly, I need to write a solution using what I wrote in the previous solution
but now with memoization that uses a dictionary in which the keys are tuples:
(len(seq),target)
For number 1 this is what I got to so far:
def subset_sum(seq, target):
if target == 0:
return True
if seq[0] == target:
return True
if len(seq) > 1:
return subset_sum(seq[1:],target-seq[0]) or subset_sum(seq[1:],target)
return False
Not sure I got it right so if I could get some input I will be grateful.
For number 2:
def subset_sum_mem(seq, target, mem=None ):
if not mem:
mem = {}
key=(len(seq),target)
if key not in mem:
if target == 0 or seq[0]==target:
mem[key] = True
if len(seq)>1:
mem[key] = subset_sum_mem(seq[1:],target-seq[0],mem) or subset_sum_mem(seq[1:],target,mem)
mem[key] = False
return mem[key]
I can't get the memoization to give me the correct answer so I'd be glad for some guidance here.
Thanks for anyone willing to help!
Just for reference, here's a solution using dynamic programming:
def positive_negative_sums(seq):
P, N = 0, 0
for e in seq:
if e >= 0:
P += e
else:
N += e
return P, N
def subset_sum(seq, s=0):
P, N = positive_negative_sums(seq)
if not seq or s < N or s > P:
return False
n, m = len(seq), P - N + 1
table = [[False] * m for x in xrange(n)]
table[0][seq[0]] = True
for i in xrange(1, n):
for j in xrange(N, P+1):
table[i][j] = seq[i] == j or table[i-1][j] or table[i-1][j-seq[i]]
return table[n-1][s]
This is how I'd write the subset_sum:
def subset_sum(seq, target):
if target == 0:
return True
for i in range(len(seq)):
if subset_sum(seq[:i] + seq[i+1:], target - seq[i]):
return True
return False
It worked on a couple of examples:
>>> subset_sum([-1,1,5,4], 0))
True
>>> subset_sum([-1,1,5,4], 10)
True
>>> subset_sum([-1,1,5,4], 4)
True
>>> subset_sum([-1,1,5,4], -3)
False
>>> subset_sum([-1,1,5,4], -4)
False
To be honest I wouldn't know how to memoize it.
Old Edit: I removed the solution with any() because after some tests I found out that to be slower!
Update: Just out of curiosity you could also use itertools.combinations:
from itertools import combinations
def com_subset_sum(seq, target):
if target == 0 or target in seq:
return True
for r in range(2, len(seq)):
for subset in combinations(seq, r):
if sum(subset) == target:
return True
return False
This can do better that the dynamic programming approach in some cases but in others it will hang (it's anyway better then the recursive approach).
I have this modified code:
def subset_sum(seq, target):
left, right = seq[0], seq[1:]
return target in (0, left) or \
(bool(right) and (subset_sum(right, target - left) or subset_sum(right, target)))
def subset_sum_mem(seq, target, mem=None):
mem = mem or {}
key = (len(seq), target)
if key not in mem:
left, right = seq[0], seq[1:]
mem[key] = target in (0, left) or \
(bool(right) and (subset_sum_mem(right, target - left, mem) or subset_sum_mem(right, target, mem)))
return mem[key]
Can you provide some test cases this does not work for?