Number of Palindromic Substrings- error in dynamic programming - string

I'm trying to solve the number of palindromic substrings problem using DP.
The problem requires us to find the number of palindromic substrings in a string.
My current code is as follows:
def palindromic_substrings(s):
result = 0
dp = [[False] * len(s) for i in range(len(s))]
for i in range(len(s)):
for j in range(i, len(s)):
dp[i][j] = j-i <= 1 or (s[i] == s[j] and dp[i+1][j-1])
if dp[i][j]:
result += 1
return result
print(palindromic_substrings("aaa")) # returns 5- should be 6
My logic is a follows:
Let dp[i][j] represents whether the substring defined by s[i:j] is a palindromic substring. There are two cases to consider to determine dp[i][j].
The first is noticing that any substring of length less than 2 is always a palindrome i.e. j-i <= 1.
The second is that any substring that has length greater than 2 is a palindrome iff the first and last characters are the same and the inner substring, excluding the first and last characters is a palindrome i.e. s[i] == s[j] and dp[i+1][j-1].
If dp[i][j] is True i.e. it is palindromic, we increment the result.
My code is returning the wrong result and I'm unsure where the gap in my logic is.
Any help would be appreciated.

This is inspired by #Stef great comments, so the working code will be like this: (credit to him, question for me... ;-)
def countSubstrings(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
ans = 0
for i in range(n-1, -1, -1):
for j in range(i, n):
dp[i][j] = s[i] == s[j] and ((j-i+1) < 3 or dp[i+1][j-1])
ans += dp[i][j]
return ans
And this is non-DP version to compare:
def countSubstrings(self, s: str) -> int:
count = 0
n = len(s)
for i in range(2 * n - 1):
left = i // 2
right = (i+1) // 2
while left >= 0 and right <n and s[left] == s[right]:
count += 1
left -= 1
right += 1
return count

Related

The algorithm receives a natural number N > 1 as input and builds a new number R from it as follows:

Python.
It's a problem:
The algorithm receives a natural number N > 1 as input and builds a new number R from it as follows:
We translate the number N into binary notation.
Invert all bits of the number except the first one.
Convert to decimal notation.
Add the result with the original number N.
The resulting number is the desired number R. Indicate the smallest odd number N for which the result of this algorithm is greater than 310. In your answer, write this number in decimal notation.
This is my solution:
for n in range(2, 10000):
s = bin(n)[2:]
for i in range(len(s)):
if s[i+1] == 0:
s[i] = '1'
else:
s[i] = 'k'
for i in range(len(s)):
if s[i] == 'k':
s[i] = '0'
h = int(s, 2)
r = h + n
if n % 2 == 1 and r > 310:
print(n)
break
So it doesn't work and i dont know why. I am now preparing for the exam, so I would be grateful if you could explain the reason to me
the bin function returns a string and my idea is to go through the binary elements of this string, starting from the second element, to replace 0 with 1, and 1 with k. Then iterate over the elements of a new line again and replace k with 0
Took me longer than I expected but feels good.
Comments might make it look chaotic but will make it easily understandable.
#since N is supposed to be odd and >1 the loop is being run from 3
for N in range(3, 10000,2):
#appending binary numbers to the list bin_li
bin_li=[]
bin_li.append((bin(N)[2:]))
for i in bin_li:
#print("bin_li item :",i)
#storing 1st digit to be escaped in j
j=i[:1]
#reversing the digits
for k in i[1:]:
if k=='0':
#putting together the digits after reversing
j=j+'1'
else:
j=j+'0'
#print("reversed item :",j) #note first digit is escaped
#converting back to decimal
dec=int(j,2)
R=dec+N
#print("current sum:---------" ,R)
if R > 310:
print("The number N :",N)
print("The reversed binary number:",dec)
print("Sum :",R)
break
#break will only break the inner loop
# for reference https://www.geeksforgeeks.org/how-to-break-out-of-multiple-loops-in-python/
else:
continue
break

Counting 9's in numbers from 1 to n

I'm trying to count all 9's in numbers from 1 to n, including repeating digits such as in 99. My code (python 3) works and returns the corrects answer for most cases except for very large numbers (like 20 digit numbers). Could someone help and let me know how this is possible?
Thanx.
def count_nines(n):
count = 0
num = [i for i in str(n)]
while len(num) > 0:
if len(num) == 1:
if num[0] == '9':
count += 1
else:
count += int(num[0]) * int(str(len(num)-1).ljust(len(num)-1, '0'))
if num[0] == '9':
count += int(''.join(num[1:]))+1
num.pop(0)
return count
The problem is in this expression:
int(str(len(num)-1).ljust(len(num)-1, '0'))
This works fine as long as str(len(num)-1) is one character, but when len(num) > 10, this is no longer the case, and then ljust will add fewer zeroes than needed. In fact, you always want to append len(num)-2 zeroes. So change this expression to:
int(str(len(num)-1) + '0' * (len(num)-2))
Simply:
def count_nines(n):
count = 0
for i in range(n+1):
if "9" in [*str(i)]:
count = count + str(i).count('9')
return count
sum((str(i).count('9') for i in range(1, n+1)))

How to iterate faster through a loop with a range that goes to at least 2 digits (10) until 1000 digits (10^100) using Python 3 +?

I receive a non-empty string S of at most 1000 characters and an integer N (1 ≤ N ≤ 1000). Each character of S is either a decimal digit or the character “ ? ”; the leftmost character is not “ 0 ” and at least one character is “ ? ”.
I have to show an integer D without leading zeros indicating the smallest multiple of N that has | S | digits and such that the digits in S are coincident with the corresponding digits in D. If there exists no such an integer D, i write an “ * ”.
So, i managed to write a code that kind do that, but it takes forever for large numbers, i want to find a way to make it faster or if i can improve my code.
Here its an example of input and output:
input: 1??????????????????????????????? 2 / output: 10000000000000000000000000000000
input: ???????????????????????????????1 2 / output: *
input: ?294?? 17 / output: 129404
my code:
D, N = input().split()
lenOfD = len(D)
lowerD = ''
higherD = ''
infos = {}
for index, letter in enumerate(D):
if letter.isdigit():
lowerD += letter
higherD += letter
infos[index] = letter
else:
if index == 0:
lowerD += '1'
higherD += '9'
else:
lowerD += '0'
higherD += '9'
rest = int(lowerD) % int(N)
quotient = int(int(lowerD) / int(N))
if rest != 0:
while True:
result = quotient * int(N)
resultString = str(result)
matchInfo = 0
if len(resultString) == lenOfD:
newRest = result % int(N)
if newRest == 0:
for position, value in infos.items():
if resultString[position] == value:
matchInfo += 1
quotient += 1
if result > int(higherD):
print('*')
break
if len(infos) == matchInfo:
print(result)
break
else:
print(lowerD)
and still have a minor problem, that when a divide a large number for a small one, the precision sucks because it approximate to scientific notation, and that is why i converted the variable quotient to int, but its not converting to the right number.

Find the maximum value of K such that sub-sequences A and B exist and should satisfy the mentioned conditions

Given a string S of length n. Choose an integer K and two non-empty sub-sequences A and B of length K such that it satisfies the following conditions:
A = B i.e. for each i the ith character in A is same as the ith character in B.
Let's denote the indices used to construct A as a1,a2,a3,...,an where ai belongs to S and B as b1,b2,b3,...,bn where bi belongs to S. If we denote the number of common indices in A and B by M then M + 1 <= K.
Find the maximum value of K such that it is possible to find the sub-sequences A and B which satisfies the above conditions.
Constraints:
0 < N <= 10^5
Things which I observed are:
The value of K = 0 if the number of characters in the given string are all distinct i.e S = abcd.
K = length of S - 1 if all the characters in the string are same i.e. S = aaaa.
The value of M cannot be equal to K because then M + 1 <= K will not be true i.e you cannot have a sub-sequence A and B that satifies A = B and a1 = b1, a2 = b2, a3 = b3, ..., an = bn.
If the string S is palindrome then K = (Total number of times a character is repeated in the string if the repeatation count > 1) - 1. i.e. S = tenet then t is repeated 2 times, e is repeated 2 times, Total number of times a character is repeated = 4, K = 4 - 1 = 3.
I am having trouble designing the algorithm to solve the above problem.
Let me know in the comments if you need more clarification.
(Update: see O(n) answer.)
We can modify the classic longest common subsequence recurrence to take an extra parameter.
JavaScript code (not memoised) that I hope is self explanatory:
function f(s, i, j, haveUncommon){
if (i < 0 || j < 0)
return haveUncommon ? 0 : -Infinity
if (s[i] == s[j]){
if (haveUncommon){
return 1 + f(s, i-1, j-1, true)
} else if (i == j){
return Math.max(
1 + f(s, i-1, j-1, false),
f(s, i-1, j, false),
f(s, i, j-1, false)
)
} else {
return 1 + f(s, i-1, j-1, true)
}
}
return Math.max(
f(s, i-1, j, haveUncommon),
f(s, i, j-1, haveUncommon)
)
}
var s = "aabcde"
console.log(f(s, s.length-1, s.length-1, false))
I believe we are just looking for the closest equal pair of characters since the only characters excluded from A and B would be one of the characters in the pair and any characters in between.
Here's O(n) in JavaScript:
function f(s){
let map = {}
let best = -1
for (let i=0; i<s.length; i++){
if (!map.hasOwnProperty(s[i])){
map[s[i]] = i
continue
}
best = Math.max(best, s.length - i + map[s[i]])
map[s[i]] = i
}
return best
}
var strs = [
"aabcde", // 5
"aaababcd", // 7
"aebgaseb", // 4
"aefttfea",
// aeft fea
"abcddbca",
// abcd bca,
"a" // -1
]
for (let s of strs)
console.log(`${ s }: ${ f(s) }`)
O(n) solution in Python3:
def compute_maximum_k(word):
last_occurences = {}
max_k = -1
for i in range(len(word)):
if(not last_occurences or not word[i] in last_occurences):
last_occurences[word[i]] = i
continue
max_k = max(max_k,(len(word) - i) + last_occurences[word[i]])
last_occurences[word[i]] = i
return max_k
def main():
words = ["aabcde","aaababcd","aebgaseb","aefttfea","abcddbca","a","acbdaadbca"]
for word in words:
print(compute_maximum_k(word))
if __name__ == "__main__":
main()
A solution for the maximum length substring would be the following:
After building a Suffix Array you can derive the LCP Array. The maximum value in the LCP array corresponds to the K you are looking for. The overall complexity of both constructions is O(n).
A suffix array will sort all prefixes in you string S in ascending order. The longest common prefix array then computes the lengths of the longest common prefixes (LCPs) between all pairs of consecutive suffixes in the sorted suffix array. Thus the maximum value in this array corresponds to the length of the two maximum length substrings of S.
For a nice example using the word "banana", check out the LCP Array Wikipage
I deleted my previous answer as I don't think we need an LCS-like solution (LCS=longest Common Subsequence).
It is sufficient to find the couple of subsequences (A, B) that differ in one character and share all the others.
The code below finds the solution in O(N) time.
def function(word):
dp = [0]*len(word)
lastOccurences = {}
for i in range(len(dp)-1, -1, -1):
if i == len(dp)-1:
dp[i] = 0
else:
if dp[i+1] > 0:
dp[i] = 1 + dp[i+1]
elif word[i] in lastOccurences:
dp[i] = len(word)-lastOccurences[word[i]]
lastOccurences[word[i]] = i
return dp[0]
dp[i] is equal to 0 when all characters from i to the end of the string are different.
I will explain my code by an example.
For "abcack", there are two cases:
Either the first 'a' will be shared by the two subsequences A and B, in this case the solution will be = 1 + function("bcack")
Or 'a' will not be shared between A and B. In this case the result will be 1 + "ck". Why 1 + "ck" ? It's because we have already satisfied M+1<=K so just add all the remaining characters. In terms of indices, the substrings are [0, 4, 5] and [3, 4, 5].
We take the maximum between these two cases.
The reason I'm scanning right to left is to not have O(N) search for the current character in the rest of the string, I maintain the index of the last visited occurence of the character in the dict lastOccurences.

Strings in python 3.7

How to count sub-strings in a string?
Example: findSubstrings("foxcatfox","fox") # should return 2
If recursion is really a must, you can try dividing the problem first.
Say if you found a matching substring at position i, then the total number of substring is 1 + findSub(string[i+1:], sub), so you can write something like this:
def findSubstringsRecursive(string, substring):
counter = 0
substringLength = len(substring)
for i in range(len(string)):
if string[i] == substring[0]:
end = i + substringLength
sub1 = string[i:end]
if substring == sub1:
return 1 + findSubstringsRecursive(string[i+1:], substring)
return 0
The following pure recursive approach is simple enough (apart from the bool->int coercion):
def findRec(s, pat):
if len(s) < len(pat): # base case should be obvious
return 0
return (pat == s[:len(pat)]) + findRec(s[1:], pat) # recurse with smaller size
>>> findSubstrings('foxcatfox', 'fox')
2
>>> findSubstrings('foxcatfox', 'foxc')
1
>>> findSubstrings('foxcat', 'dog')
0
I should note that this counts overlapping occurrences which may or may not be desired. One might also add protection against or define behaviour for an empty substring.

Resources