I have a code that will find all the cycles of a list, e.g for [1,2,3] the cycles are [1,2,3], [2,3,1], [3,1,2]. I also have a code for finding the longest increasing subsequence.
What I want to do is input a list, find the longest increasing subsequence of every cycle of that list, and then return the maximum length out of all of these. how do I go from these two functions to finding the LIS of every cycle and then return the maximum?
Here is my code so far:
def cycles(X):
n = len(X)
values = []
for i in range(0,n):
values.append(X[i:n] + X[0:i])
return values
def longest_increasing_subsequence(d):
l = []
for i in range(len(d)):
l.append(max([l[j] for j in range(i) if l[j][-1] < d[i]] or [[]], key=len) + [d[i]])
return len(max(l, key=len))
I'd appreciate any help. thanks.
This will do the job:
l=[1,2,3,4]
s=cycles(l)
lis=[longest_increasing_subsequence(d) for d in s]
print(lis)
print(max(lis))
The result is
[4,3,2,3]
and
4
Try this code:
l = [3,4,5,9,8,1,2,7,7,7,7,7,7,7,6,0,1]
empty = []
one = [1]
two = [2,1]
three = [1,0,2,3]
tricky = [1,2,3,0,-2,-1]
ring = [3,4,5,0,1,2]
internal = [9,1,2,3,4,5,0]
# consider your list as a ring, continuous and infinite
def longest_increasing_subsequence(l):
length = len(l)
if length == 0: return 0 # list is empty
i, tmp, longest = [0, 1, 1]
# 1 < tmp means that ring is finished, but the sequence continue to increase
while i < length or 1 < tmp:
# compare elements on the ring
if l[i%length] < l[(i+1)%length]:
tmp += 1
else:
if longest < tmp: longest = tmp
tmp = 1
i += 1
return longest
print("0 == " + str(longest_increasing_subsequence(empty)))
print("1 == " + str(longest_increasing_subsequence(one)))
print("2 == " + str(longest_increasing_subsequence(two)))
print("3 == " + str(longest_increasing_subsequence(three)))
print("5 == " + str(longest_increasing_subsequence(tricky)))
print("5 == " + str(longest_increasing_subsequence(internal)))
print("6 == " + str(longest_increasing_subsequence(ring)))
print("6 == " + str(longest_increasing_subsequence(l)))
Related
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
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 would I go about reading integers until -1 is input and then printing the length of the longest continuous sequence of numbers where there are
alternating odd then even numbers?
I've achieved the first section but it went downhill from there.
Some testing lists:
[1,2,3,4,5,10,6,7,8,20,25,30,40,-1]
[6,7,8,20,25,30,40,1,2,3,4,5,10,15,20,-1]
Here is my code:
evenOdd=[]
while True:
try:
n=int(input())
if n != -1:
evenOdd.append(n)
except:
break
evenOdd=[]
longest = 0
length = 0
for i in range(len(evenOdd)):
if ((evenOdd[i-2]% 2 == 0) and (evenOdd[i-1]% 2 == 1) and (evenOdd[i]% 2 == 0):
length += 1
else:
longest = max(longest, length)
length = 0
print(longest)
One option would be to keep track of the longest sequence as you go:
longest = []
current = []
while True:
n = int(input("Enter value: "))
if n == -1:
break
if current and current[-1] % 2 != n % 2:
current.append(n)
else:
current = [n]
if len(current) > len(longest):
longest = current
The upside here is there's no post-processing to be done when the -1 is entered, the result is ready to go.
You can use itertools.cycle to alternate between a remainder of 0 and 1, and use itertools.groupby to group odd-even sequences:
from itertools import groupby, cycle
l = [1,2,3,4,5,10,6,7,8,20,25,30,40]
r = cycle((0, 1))
print(max(sum(1 for i in g) for _, g in groupby(l, key=lambda n: n % 2 == next(r))))
This outputs: 6 (since the longest odd-even sequence is 1,2,3,4,5,10)
This is how I did. I think this might be simpler than the above examples.
def alternating(lst):
longSeries = []
currentSeries=[]
for i in range (len(lst)-1):
if i == 0:
currentSeries = [lst[0]]
if(abs(lst[i] - lst[i+1]) % 2 == 1):
currentSeries.append(lst[i+1])
else:
currentSeries = [lst[i+1]]
if(len(currentSeries) > len(longSeries)):
longSeries = currentSeries
print ("The longest series is: " +str(longSeries))
print(len(longSeries))
You can apply itertools.groupby twice:
import itertools
d = [[1,2,3,4,5,10,6,7,8,20,25,30,40,-1], [6,7,8,20,25,30,40,1,2,3,4,5,10,15,20,-1]]
def key_func(d):
start= not d[0]%2
for i in d[1:]:
if i%2 == start:
start = (not i%2)
else:
return False
return True
for l in d:
new_l = [list(b) for _, b in itertools.groupby(l, key=lambda x:x%2)]
second_l = [[i for [i] in b] for a, b in itertools.groupby(new_l, key=lambda x:len(x) ==1) if a]
print(max(second_l, key=lambda x:[key_func(x), len(x)]))
Output:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 10, 15, 20, -1]
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.
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)