While trying this question: https://leetcode.com/problems/permutation-in-string/
"Given two strings s1 and s2, return true if s2 contains a permutation
of s1, or false otherwise.
In other words, return true if one of s1's permutations is the
substring of s2."
I came up with a sliding window solution, but I did not want to use an additional hash table for keeping track of the letters in the current window. I am struggling to figure out why it is not correct.
Fails with this example: "trinitrophenylmethylnitramine" "dinitrophenylhydrazinetrinitrophenylmethylnitramine"
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
s1_ht = {}
for char in s1:
if char in s1_ht:
s1_ht[char] += 1
else:
s1_ht[char] = 1
start = 0
counter = len(s1_ht)
for i in range(0, len(s2), 1):
if s2[i] in s1_ht:
s1_ht[s2[i]] -= 1
if s1_ht[s2[i]] == 0:
counter -= 1
if i - start + 1 == len(s1):
if counter == 0:
return True
if s2[start] in s1_ht:
s1_ht[s2[start]] += 1
if s1_ht[s2[start]] > 0:
counter += 1
start += 1
return False
Great solution. But there is a minor mistake here:
if s1_ht[s2[start]] > 0:
counter += 1
In these lines of code, you increment the counter although s1_ht[ s2[start] ] was not zero before the incrementation. Let me explain with an example. Suppose s1_ht['t'] = 4. Then you find s2[start] = 't'. Therefore you increment s1_ht['t'] and set it equal to 5. But then you increment the counter which leads to an error. You shouldn't increment the counter since it already covers the character 't'. Swap that line with this:
if s1_ht[s2[start]] == 1: # just before the incrementation, it was zero
counter += 1
It passes the Leetcode test cases with 61 ms runtime and 14 MB memory on my machine.
Related
Given an array of integers arr of even length n and an integer k.
We want to divide the array into exactly n/2 pairs such that the sum of each pair is divisible by k.
Return True If you can find a way to do that or False otherwise.
e.g.
arr = [1,2,3,4,5,10,6,7,8,9], k = 5, output: True
arr = [-1,-1,-1,-1,2,2,-2,-2], k = 3, output: False
The code i've written:
def canArrange(self, arr: List[int], k: int) -> bool:
dictt = {}
for i in range(len(arr)):
arr[i] = arr[i] % k
if arr[i] not in dictt:
dictt[arr[i]] = 1
else:
dictt[arr[i]] += 1
count = 0
for num in arr:
if (k-num) == num:
if dictt[num] > 1:
dictt[num] -= 2
count += 1
else:
dictt[num] -= 1
if (k-num) in dictt and dictt[(k-num)] != 0 and dictt[num] != 0:
dictt[(k-num)] -= 1
dictt[num] -= 1
count += 1
elif num == 0 and dictt[num] > 1:
dictt[num] -= 2
count += 1
if count == len(arr)//2:
return True
else:
return False
My logic is I am first taking the count of "every element % k" and the finding its another pair and reducing the count of both to avoid duplicates, one special case is when the values in a pair are same so for that i am already decrementing the value in my dictionary and at last i am checking whether the number of pairs are equal to half of elements or not
I am able to pass 93/97 test cases on leetcode, and unable to debug for the rest as the length of these 4 test cases are huge hence not possible manually, i must be missing some points while setting my logic, kindly help
Edit: DONE (I have edited the code above)
I'm playing with the Codality Demo Task. It's asking to design a function which determines the lowest missing integer greater than zero in an array.
I wrote a function that works, but Codility tests it as 88% (80% correctness). I can't think of instances where it would fail.
def solution(A):
#If there are negative values, set any negative values to zero
if any(n < 0 for n in A):
A = [(i > 0) * i for i in A]
count = 0
else: count = 1
#Get rid of repeating values
A = set(A)
#At this point, we may have only had negative #'s or the same # repeated.
#If negagive #'s, or repeated zero, then answer is 1
#If repeated 1's answer is 2
#If any other repeated #'s answer is 1
if (len(A) == 1):
if (list(A)[0] == 1):
return 2
else:
return 1
#Sort the array
A = sorted(A)
for j in range(len(A)):
#Test to see if it's greater than zero or >= to count. If so, it exists and is not the lowest integer.
#This fails if the first # is zero and the second number is NOT 1
if (A[j] <= count or A[j] == 0): #If the number is lt or equal to the count or zero then continue the count
count = count + 1
elif (j == 1 and A[j] > 1): return 1
else:
return count
return count
UPDATE:
I got this to 88% with the fixes above. It still fails with some input. I wish Codility would give the inputs that fail. Maybe it does with a full subscription. I'm just playing with the test.
UPDATE 2: Got this to 100% with Tranbi's suggestion.
def solution(A):
#Get rid of all zero and negative #'s
A = [i for i in A if i > 0]
#At this point, if there were only zero, negative, or combination of both, the answer is 1
if (len(A) == 0): return 1
count = 1
#Get rid of repeating values
A = set(A)
#At this point, we may have only had the same # repeated.
#If repeated 1's answer is 2
#If any other repeated #'s only, then answer is 1
if (len(A) == 1):
if (list(A)[0] == 1):
return 2
else:
return 1
#Sort the array
A = sorted(A)
for j in range(len(A)):
#Test to see if it's >= to count. If so, it exists and is not the lowest integer.
if (A[j] <= count): #If the number is lt or equal to the count then continue the count
count = count + 1
else:
return count
return count
Besides that bug for len=1, you also fail for example solution([0, 5]), which returns 2.
Anyway... Since you're willing to create a set, why not just make this really simple?
def solution(A):
A = set(A)
count = 1
while count in A:
count += 1
return count
I don't think this is true:
#At this point, we may have only had negative #'s or the same # repeated. If so, then the answer is 1+ the integer.
if (len(A) == 1):
return list(A)[0]+1
If A = [2] you should return 1 not 3.
Your code is quite confusing though. I think you could replace
if any(n < 0 for n in A):
A = [(i > 0) * i for i in A]
with
A = [i for i in A if i > 0]
What's the point of keeping 0 values?
I don't think this is needed:
if (len(A) == 1):
if (list(A)[0] == 1):
return 2
else:
return 1
It's already taken into account afterwards :)
Finally got a 100% score.
def solution(A):
# 1 isn't there
if 1 not in A:
return 1
# it's easier to sort
A.sort()
# positive "hole" in the array
prev=A[0]
for e in A[1:]:
if e>prev+1>0:
return prev+1
prev=e
# no positive "hole"
# 1 is in the middle
return A[-1]+1
Is there any recursive way to implement minimum number of adjacent swaps to convert a string into its given anagram in particular this solution?
I have written a solution in Python but I don't know how to implement it using recursion.
def min_adjacent_swaps(r1, r2):
s1 = list(r1)
s2 = list(r2)
i = 0
j = 0
result = 0
while i < len(s2):
j = i
while s1[j] != s2[i]:
j += 1
while i < j:
temp = s1[j]
s1[j] = s1[j - 1]
s1[j - 1] = temp
j -= 1
result += 1
i += 1
return result
>>> print(min_adjacent_swaps("abcd", "badc"))
2
My aproach to this problem would be turn while i < len(s2) into if i < len(s2) and then figure out how to use the original function to finish the solution. I.e we've solved for the first character, now recurse for the rest. Doing that, optimizing the result, and throwing some Python at it, I get something like:
def min_adjacent_swaps(r1, r2):
# r1 and r2 can be str or list
swaps = 0
if r1 and r2:
s1 = list(r1) # make mutable
j = s1.index(r2[0])
while j > 0:
s1[j-1], s1[j] = s1[j], s1[j-1] # swap
swaps += 1
j -= 1
return swaps + min_adjacent_swaps(s1[1:], r2[1:])
return swaps
print(min_adjacent_swaps("abcd", "dcba"))
print(min_adjacent_swaps("abcd", "badc"))
Looking at your original, and my rewrite, I can't say whether either returns the minimum number of steps. But this was really about how to convert an iterative solution to a recursive one.
I have a long string of characters and not only am I trying to find if a substring of those characters exists within the larger string, I'm also trying to find the longest run of successive instances.
For example... in the code snippet below I've found that I can use "count" to see how many times the substring b appears in a. That result is 5. However, what I'm trying to identify is the longest successive run, which would be 3 (where 'abc' appears back to back to back in the middle). I'm having difficulty running through the logic of this one. Any advice would be appreciated.
a = "abcxyzabcabcabcxyzabcxyz"
b = "abc"
total = a.count(b)
print(total)
This should be fairly simple with a while loop:
def func(a, b):
n = 1
while b*n in a:
n += 1
return n - 1
One possible and naive solution is to use the python index function to identify the closest index of the substring. From there you can simply continue searching ahead for the substring until you find a point where it doesnt appear anymore, then call index again to skip ahead.
Example:
a = "abcxyzabcabcabcxyzabcxyz"
b = "abc"
curr_index = a.index(b)
longest_count = 0
current_count = 0
while curr_index < len(a):
if a[curr_index : curr_index + len(b)] == b:
curr_index += len(b)
current_count += 1
else:
if longest_count < current_count:
longest_count = current_count
try:
curr_index = a.index(b, curr_index)
except ValueError:
# Substring no longer found in string slice
break
current_count = 0
if longest_count < current_count:
longest_count = current_count
print(longest_count)
This just returns the longest repeating count, but it doesn't return the location where it starts. Adding that functionality, however, is trivial.
Keep calling a.index on b with the appropriate indices. If the index is the start of your subset, you're in the same run. Otherwise, start a new run:
def longest_run(string, pattern):
longest = 0
current = 0
start = 0
while True:
try:
ind = string.index(pattern, start)
if ind == start:
current += 1
else:
if current > longest:
longest = current
current = 1
start += len(pattern)
except ValueError:
return longest
You can use re.findall with a pattern that matches one or more times of b (use re.escape to prevent b from being interpreted as regex), then map the returning strings to len and pass them to max to obtain the length of the longest match, and then divide that length by the length of b to get the number of times b is repeated:
import re
max(map(len, re.findall('(?:%s)+' % re.escape(b), a))) // len(b)
I am trying to solve the 'Love-Letter' mystery problem of HackerRank using Python, but I am stuck at a place where in my loop a variable is not getting updated.
s = input()
first_char = s[0]
last_char = s[-1]
ascii_first_char = ord(first_char)
ascii_last_char = ord(last_char)
count = 0
i = 1
while ascii_first_char < ascii_last_char:
count += abs((ascii_last_char-ascii_first_char))
ascii_first_char = ord(s[i])
ascii_last_char = ord(s[-i])
i += 1
print(count)
If you try to run that, you would see that alc is not changing it's value according to ord(s[i]) where I keeps incrementing. Why is that happening?
You get the first letter with s[0] and the last with s[-1]. In your loop you take the next letters with the same index i.
I don't understand your condition in the while loop. Instead of "ascii_first_char < ascii_last_char" you should test if you have looked at every element of the string. For that we have to loop len(s)/2 times. Something like:
while i < len(s) - i:
or equivalent
while 2*i < len(s):
And this conditions only work for even length. I prefer for-loops when I know how many times I will loop
current_line = input()
# if length is even, we don't care about the letter in the middle
# abcde <-- just need to look for first and last 2 values
# 5 // 2 == 2
half_length = len(current_line) // 2
changes = 0
for i in range(index):
changes += abs(
ord(current_line[i]) - ord(current_line[-(i+1)])
)
print (changes)
s1 = ['abc','abcba','abcd','cba']
for s in s1:
count = 0
i = 0
j = -1
median = len(s)/2
if median == 1:
count += abs(ord(s[0])-ord(s[-1]))
else:
while i < len(s)/2:
count += abs(ord(s[j])-ord(s[i]))
i += 1
j -= 1
print(count)