Longest palindrome in Python - python

I have a Python assignment which wants me write a program that finds the longest palindrome in a given text. I know there are examples of this function in other languages on this website, but I am a total beginner in Python and am having trouble writing the code.
This is how I am currently identifying palindromes:
def is_palindrome(word):
x = 0
for i in range (len(word)/2):
if (word[x]) == (word[len(word)-x-1]):
x+=1
if x == (len(word)/2):
return True
return False

Alternate way
def Is_palindrome(word):
return word==word[::-1]
# Assuming text is defined
print max((word for word in set(text.split()) if Is_Palindrome(word)), key=len)

I used:
def Is_palindrome(word):
x = 0
for i in range (len(word)/2):
if (word[x]) == (word[len(word)-x-1]):
x+=1
if x == (len(word)/2):
return True
return False
def longest_palindrome(text):
lst = text.split() #Split it into words (cannot have punctuation)
palindromes = [] #List that contains the palindromes
long_len = 0 #Length of the longest palindrome
longest = "" #The actual longest palindrome
for i in lst: #Loop through all the words
if Is_palindrome(i): #If the word is a palindrome
palindromes.append(i) #Add it to the palindrome list
for i in palindromes: #Loop through the palindrome list
if len(i) > long_len: #If the palindrome is longer than the longest one
longest = i #Set it as the longest one
longest_len = len(i) # Set the length of the longest one to the length of this one
return longest

def fastLongestPalindromes(seq):
seqLen = len(seq)
l = []
i = 0
palLen = 0
while i < seqLen:
if i > palLen and seq[i - palLen - 1] == seq[i]:
palLen += 2
i += 1
continue
l.append(palLen)
s = len(l) - 2
e = s - palLen
for j in range(s, e, -1):
d = j - e - 1
if l[j] == d:
palLen = d
break
l.append(min(d, l[j]))
else:
palLen = 1
i += 1
l.append(palLen)
lLen = len(l)
s = lLen - 2
e = s - (2 * seqLen + 1 - lLen)
for i in range(s, e, -1):
d = i - e - 1
l.append(min(d, l[i]))
return l
def getPalindrome(text):
lengths = fastLongestPalindromes(text)
start = 0
end = 0
length = 0
for i in range(len(lengths)):
if(lengths[i] > length):
length = lengths[i]
end = i//2+(lengths[i]//2)
start = i//2-(lengths[i]//2)
if(i%2 == 1):
start +=1
return text[start:end]
In linear time. (longer code, but faster than the other answers, atleast for long strings).
Source: http://www.akalin.cx/longest-palindrome-linear-time (first function is copy pasted)

Related

Given a string of digits, return the longest substring with alternating odd/even or even/odd digits

So I was doing this python challenge for fun, but for some reason it is saying I am incorrect and I have no idea why. The challenge prompt said this:
Given a string of digits, return the longest substring with alternating odd/even or even/odd digits. If two or more substrings have the same length, return the substring that occurs first.
Examples
longest_substring("225424272163254474441338664823") ➞ "272163254"
# substrings = 254, 272163254, 474, 41, 38, 23
longest_substring("594127169973391692147228678476") ➞ "16921472"
# substrings = 94127, 169, 16921472, 678, 476
longest_substring("721449827599186159274227324466") ➞ "7214"
# substrings = 7214, 498, 27, 18, 61, 9274, 27, 32
# 7214 and 9274 have same length, but 7214 occurs first.
Notes
The minimum alternating substring size is 2.
The code I wrote for a solution was this:
def longest_substring(digits):
substring = []
final = []
loop = 0 # just loops through the for loop
start = 0
loop2 = 0
while loop+1 < len(digits):
num = int(digits[loop])
num2 = int(digits[loop+1])
if (num + num2)%2 != 0 and start == 0:
substring.append(num)
substring.append(num2)
start += 1
elif (num + num2)%2 != 0:
substring.append(num2)
else:
start = 0
loop2 += 1
final.append(substring.copy())
substring.clear()
loop += 1
sorted_list = list(sorted(final, key=len))
if len(sorted_list[-1]) == len(sorted_list[-2]):
index1 = final.index(sorted_list[-1])
index2 = final.index(sorted_list[-2])
if index1 < index2: # because the larger than not first
sorted_list = sorted_list[-1]
else:
sorted_list = sorted_list[-2]
sorted_list = str(sorted_list)
sorted_list = sorted_list.replace('[','')
sorted_list = sorted_list.replace(']', '')
sorted_list = sorted_list.replace(', ','')
return str(sorted_list) # or print(str(sorted_list)) neither works
If you're curious the challenge is here
I would actually prefer using a shorter code. It's easier to look for errors IMO.
Does this work for you?
def longest_substring(digits):
max_len = 0
ans = ''
for i in range(len(digits)):
temp = digits[i]
for x in range(i+1, len(digits)):
if int(digits[x])%2 != int(digits[x-1])%2:
temp += digits[x]
else:
break
if len(temp) > max_len:
max_len = len(temp)
ans = temp
return ans
Here's something a bit simpler:
def longest_substring(digits):
current = longest = digits[0]
for digit in digits[1:]:
if int(digit)%2 != int(current[-1])%2:
current += digit
else:
longest = longest if len(longest) >= len(current) else current
current = digit
longest = longest if len(longest) >= len(current) else current
return longest
The bit about how you're picking the indexes at the end doesn't always work, leading to cases where your list ends up with all of the possibilities in it. If, instead you modify the code to be explicit, and run through the entire list after you create sorted_list:
best = []
for cur in sorted_list:
if len(cur) > len(best):
best = cur
return "".join([str(x) for x in best])
The rest of your implementation will work.
And for kicks, I took a pass at simplifying it:
def longest_substring(digits):
possible, best = "", ""
for x in digits + " ":
if x == " " or (len(possible) > 0 and int(possible[-1]) % 2 == int(x) % 2):
best = possible if len(possible) > len(best) else best
possible = ""
possible += x
return best

Find all the common substrings between two strings, regardless of case and order

So i started with the code from an answer to this question Function to find all common substrings in two strings not giving correct output and modified it a little bit to accommodate case-independence (i.e. AbCd is the same as ABCD as Abcd and so on) by turning the string to lowercase. However, for strings like 'ABCDXGHIJ' and 'ghijYAbCd', it only returns ['ghij'], not the desired output ['ABCD', 'GHIJ'].
Here are other examples:
'Bonywasawarrior' and 'Bonywasxwarrior' (output: ['Bonywas', 'warrior', 'wa'], desired output: ['Bonywas', 'warrior'])
'01101001' and '101010' (output: ['1010', '0', '1010', '01', '10', '01'], desired output: ['1010'])
here is my code:
t = int(input()) #t cases
while t > 0:
A = str(input()) #1st string
B = str(input()) #2nd string
low_A = A.lower()
low_B = B.lower()
answer = ""
anslist=[]
for i in range(len(A)):
common = ""
for j in range(len(B)):
if (i + j < len(A) and low_A[i + j] == low_B[j]):
common += B[j]
else:
#if (len(common) > len(answer)):
answer = common
if answer != '' and len(answer) > 1:
anslist.append(answer)
common = ""
if common != '':
anslist.append(common)
if len(anslist) == 0:
print('[]') #print if no common substring
else:
print(anslist)
t -= 1
You can increment an offset in a while loop to keep concatenating common characters at the offset from the respective indices until they become different instead. To find the longest, non-overlapping common substrings, you can use a function that recursively traverses different paths of substring partitioning, and returns the one with the longest lengths of substrings:
def common_strings(a, b, i=0, j=0):
candidates = []
len_a = len(a)
len_b = len(b)
if j == len_b:
candidates.append(common_strings(a, b, i + 1, 0))
elif i < len_a:
offset = 0
while i + offset < len_a and j + offset < len_b and a[i + offset].lower() == b[j + offset].lower():
offset += 1
if offset > 1:
candidates.append([a[i: i + offset]] + common_strings(a, b, i + offset, j + offset))
candidates.append(common_strings(a, b, i, j + 1))
return candidates and max(candidates, key=lambda t: sorted(map(len, t), reverse=True))
so that:
print(common_strings('ABCDXGHIJ', 'ghijYAbCd'))
print(common_strings('Bonywasawarrior', 'Bonywasxwarrior'))
print(common_strings('01101001', '101010'))
outputs:
['ABCD', 'GHIJ']
['Bonywas', 'warrior']
['1010']
This is a duplicate of Finding all the common substrings of given two strings, which offers a solution in Java and for which I have done my best to translate to Python with the "enhancement" of making it case-insensitive:
def find_common(s, t):
table = [len(t)*[0] for i in range(len(s))]
longest = 0
result = set()
for i, ch1 in enumerate(s.lower()):
for j, ch2 in enumerate(t.lower()):
if ch1 != ch2:
continue
table[i][j] = 1 if i == 0 or j == 0 else 1 + table[i - 1][j - 1]
if table[i][j] > longest:
longest = table[i][j]
result.clear()
if table[i][j] == longest:
result.add(s[i - longest + 1:i + 1]);
return result
print(find_common('Bonywasawarrior', 'Bonywasxwarrior'))
print(find_common('01101001', '101010'))
print(find_common('ABCDXGHIJ', 'ghijYAbCd'))
Prints:
{'Bonywas', 'warrior'}
{'1010'}
{'GHIJ', 'ABCD'}

How to delete specific elements from an array while not deleting its later occurences

Take integer input from a user and then delete the elements from an array having those many consecutive ocurences from the array.
Eg the input array is "aabcca" and the input from the user is 2.
Then the answer should be "ba".
I tried it when the elements are not repeated. My code works perfectly for examples like "aaabbccc".
for j in range(t, (n+1)):
if (t == n):
if (count == k):
array = [x for x in array if x != temp]
print array
exit()
if (t == n and count == k):
array = [x for x in array if x != temp]
print array
exit()
if temp == data[j]:
count += 1
t += 1
if temp != data[j]:
if count == k:
array = [x for x in array if x != temp]
temp = data[t]
count = 1
t += 1
you can use sliding window or two pointers to solve it.
the key point is use [start, end] range to record a consecutive seq, and only add the seq whose length less than n:
def delete_consecutive(s, n):
start, end, count = 0, 0, 0
res, cur = '', ''
for end, c in enumerate(s):
if c == cur:
count += 1
else:
# only add consecutive seq less than n
if count < n:
res += s[start:end]
count = 1
start = end
cur = c
# deal with tail part
if count < n:
res += s[start:end+1]
return res
test and output:
print(delete_consecutive('aabcca', 2)) # output: ba
print(delete_consecutive('aaabbccc', 3)) # output: bb
Hope that helps you, and comment if you have further questions. : )
Here is one way to do that:
def remove_consecutive(s, n):
# Number of repeated consecutive characters
count = 0
# Previous character
prev = None
# Pieces of string of result
out = []
for i, c in enumerate(s):
# If new character
if c != prev:
# Add piece of string without repetition blocks
out.append(s[i - (count % n):i])
# Reset count
count = 0
# Increase count
count += 1
prev = c
# Add last piece
out.append(s[len(s) - (count % n):])
return ''.join(out)
print(remove_consecutive('aabcca', 2))
# ba
print(remove_consecutive('aaabbccc', 2))
# ac
print(remove_consecutive('aaabbccc', 3))
# bb

Ordered ranking for a list of permutations

I am trying to develop a method for finding the orderd rank of a particular sequence in the following lists.
a = list(sorted(itertools.combinations(range(0,5),3)))
b = list(sorted(itertools.permutations(range(0,5),3)))
a represents a list of elemnts of a combinatorial number system so the formula for rank is pretty straight forward.
What I need are 2 function magic_rank_1 and magic_rank_2 which have the following definitions
def magic_rank_1(perm_item,permutation_list_object):
## permutation_list_object is actually b
return list_object.index(perm_item)
def magic_rank_2(perm_item,permutation_list_object,combination_list_object):
## permutation_list_object is actually b and combination_list_object is actually a
return combination_list_object.index(tuple(sorted(perm_item)))
So basically magic_rank_2((0,1,2),b,a) = magic_rank_2((2,0,1),b,a)
Sounds easy.. but i have a few restrictions.
I cant use the indexof function because I cannot afford to search lists >100000000 items for every item
I need magic_rank_1 and magic_rank_2 to be purely mathematical without using any sort function or comparison functions or search function. All the information I will have is the tuple whose rank needs to be identified and the last letter of my alphabet (in this case that will be 5)
magic rank 2 need not be a number between 0 and k-1 when k = len(a) as long as it is a unique number between 0 and 2^(ceiling((max_alphabet/2)+1))
I know magic_rank_1 can be calculated by something similar to this but there is a difference ,there every letter of the input alphabet is used, in my case it is a subset
Lastly yes this is supposed to be a substitute for a hashing function, currently using a hashing function but I am not taking advantage of the fact that magic_rank_2((0,1,2),b,a) = magic_rank_2((2,0,1),b,a) . If i can it will reduce my storage space requirements significantly as the length of my sequences is actually 5 , so if I can calculate a method for magic_rank_2 I reduce my storage requirement to 1% of current requirement
UPDATE
- For magic_rank_2 there should be no comparison operation between elements of the tuple, i.e no sorting, min,max etc
that only makes the algorithm less efficient than regular hashing
The following two functions will rank a combination and permutation, given a word and an alphabet (or in your case, a tuple and a list).
import itertools
import math
def rank_comb(word, alph, depth=0):
if not word: return 0
if depth == 0:
word = list(word)
alph = sorted(alph)
pos = 0
for (i,c) in enumerate(alph):
if c == word[0]:
# Recurse
new_word = [x for x in word if x != c]
new_alph = [x for x in alph if x > c]
return pos + rank_comb(new_word, new_alph, depth+1)
else:
num = math.factorial(len(alph)-i-1)
den = math.factorial(len(alph)-i-len(word)) * math.factorial(len(word)-1)
pos += num // den
def rank_perm(word, alph, depth=0):
if not word: return 0
if depth == 0:
word = list(word)
alph = sorted(alph)
pos = 0
for c in alph:
if c == word[0]:
# Recurse
new_word = [x for x in word if x != c]
new_alph = [x for x in alph if x != c]
return pos + rank_perm(new_word, new_alph, depth+1)
else:
num = math.factorial(len(alph)-1)
den = math.factorial(len(alph)-len(word))
pos += num // den
#== Validation =====================================================================
# Params
def get_alph(): return range(8)
r = 6
a = list(sorted(itertools.combinations(get_alph(), r)))
b = list(sorted(itertools.permutations(get_alph(), r)))
# Tests
PASS_COMB = True
PASS_PERM = True
for (i,x) in enumerate(a):
j = rank_comb(x, get_alph())
if i != j:
PASS_COMB = False
print("rank_comb() FAIL:", i, j)
for (i,x) in enumerate(b):
j = rank_perm(x, get_alph())
if i != j:
PASS_PERM = False
print("rank_perm() FAIL:", i, j)
print("rank_comb():", "PASS" if PASS_COMB else "FAIL")
print("rank_perm():", "PASS" if PASS_PERM else "FAIL")
The functions are similar, but there are few differences:
new_alph is filtered differently.
The calculation of both num and den are different.
Update:
rank_comb2 doesn't require sorting the input word (a 3-tuple):
import itertools
import math
def rank_comb2(word, alph, depth=0):
if not word: return 0
if depth == 0:
word = list(word)
alph = sorted(alph)
pos = 0
for (i,c) in enumerate(alph):
if c == min(word):
# Recurse
new_word = [x for x in word if x != c]
new_alph = [x for x in alph if x > c]
return pos + rank_comb2(new_word, new_alph, depth+1)
else:
num = math.factorial(len(alph)-i-1)
den = math.factorial(len(alph)-i-len(word)) * math.factorial(len(word)-1)
pos += num // den
r1 = rank_comb2([2,4,1], range(5))
r2 = rank_comb2([1,4,2], range(5))
r3 = rank_comb2([4,1,2], range(5))
print(r1, r2, r3) # 7 7 7

How to count common letters in order between two words in Python?

I have a string pizzas and when comparing it to pizza - it is not the same. How can you make a program that counts common letters (in order) between two words, and if it's a 60% match then a variable match is True?
For e.g. pizz and pizzas have 4 out of 6 letters in common, which is a 66% match, which means match must be True, but zzip and pizzasdo not have any letters in order in common, thus match is False
You can write a function to implement this logic.
zip is used to loop through the 2 strings simultaneously.
def checker(x, y):
c = 0
for i, j in zip(x, y):
if i==j:
c += 1
else:
break
return c/len(x)
res = checker('pizzas', 'pizz') # 0.6666666666666666
def longestSubstringFinder(string1, string2):
answer = ""
len1, len2 = len(string1), len(string2)
for i in range(len1):
match = ""
for j in range(len2):
if (i + j < len1 and string1[i + j] == string2[j]):
match += string2[j]
else:
if (len(match) > len(answer)): answer = match
match = ""
return answer
ss_len = len(longestSubstringFinder("pizz", "pizzas"))
max_len = max(len("pizza"),len("pizzas"))
percent = ss_len/max_len*100
print(percent)
if(percent>=60):
print("True");
else:
print("False")
Optimised algorithm using dynamic programming:
def LCSubStr(X, Y, m, n):
LCSuff = [[0 for k in range(n+1)] for l in range(m+1)]
result = 0
for i in range(m + 1):
for j in range(n + 1):
if (i == 0 or j == 0):
LCSuff[i][j] = 0
elif (X[i-1] == Y[j-1]):
LCSuff[i][j] = LCSuff[i-1][j-1] + 1
result = max(result, LCSuff[i][j])
else:
LCSuff[i][j] = 0
return result
This will directly return the length of LCS.

Categories

Resources