Find occurrence of a string in another string - python

Details:
There are two strings x and y.
Count the number of occurrence of y in x as follows:
Length of y is 3.
Increment the "count" value when y == x[i] x[i+2] x[i+4]
Example:
x = "aabbcc"
y = "abc"
output: 2
My Code:
def solution(x, y):
i, count = 0, 0
j = i + 2
k = i + 4
while i+4 < len(x):
cur = x[i]
while i < len(x) and i != j:
i += 1
while i < len(x) and i != k:
i += 1
count += 1
return count
solution(x, y)
I am getting count = 1. It should give count = 2

There's a couple of logic errors in your code.
The problem happens here:
while i < len(x) and i != j:
i += 1
res.append(x[i])
You keep increasing i until it is either len(x) or greater, or until it is the same as j. But since you set j to be 2 at the start (and never update it), it will simply end up setting i to len(x). And x[i] will thus fail, since x[len(x)] tries to index an element just outside x.
However, there's a few more remarks to make:
you collect what you find in res, but really only want a number (e.g. 2) as a result
you define count but don't use it
you track the coordinates in the string in three separate variables (i, j, k) and have a lot of logic to increment the first, but really all you need is to step through the string one position at a time, and look at the offsets directly
Given all that and the problem description, you were probably going for something like this:
x = "aabbcc"
y = "abc"
def solution(x, y):
i, count = 0, 0
while i + 4 < len(x):
if (x[i], x[i+2], x[i+4]) == (y[0], y[1], y[2]):
count += 1
i += 1
return count
print(solution(x, y))
However, Python has some cleverness that would make it even simpler (or at least shorter):
def solution(x, y):
count = 0
for i in range(len(x)-4):
if x[i:i+5:2] == y: # slicing with a stride of two, instead of direct indexing
count += 1
return count
Or even:
def solution(x, y):
return len([x for i in range(len(x)-4) if x[i:i+5:2] == y])
But that's favouring brevity over readability a bit too much, I feel.

A generator expression solution, taking advantage of True/False == 1/0 in a numeric context:
def solution(x, y):
return sum(y == x[i:i+5:2] for i in range(len(x)-4))

Increment the "count" value when y == x[i] x[i+2] x[i+4]
This is the same as simply creating the string consisting of x[0], x[2], x[4]... (every even-numbered character) and the string consisting of x[1], x[3], x[5]... (every odd-numbered character); counting the occurrences of y in each; and adding those two results together.
Creating the strings is trivial, and a common duplicate. Counting occurrences of a substring is also well-trodden ground. Putting these tools together:
def spread_substrings(needle, haystack):
even_haystack = haystack[::2]
odd_haystack = haystack[1::2]
return even_haystack.count(needle) + odd_haystack.count(needle)

Related

How to make the output return a number?

I created this code so that the program will print out all numbers within range (1000 to 10,000) if it is divisible by value k, as set below, but the ouput yields none.. what am I doing wrong?
k = 6
def pincode(k: int):
for x in range(1000,10000):
if x // k == 0:
print(x)
print(pincode(k))
what am I supposed to change to make sure that the code prints out all numbers within the range divisible by k?
There are two bugs, here for printing the function, you need to return value. If you've written print already then just call the function. If you want to print k for x%k==0 then x has multiple values. You can return multiple values by collecting x values to list. The second one is, it is x%k==0 and not x//k==0. // gives you whole number quotient and % will give you remainder. Eg, 49//7 is 7 and 49%7 is 0 and 26//7 is 3 and 26%7 is 5. Your new code:
k = 6
def pincode(k: int):
collect=[]
for x in range(1000,10000):
if x % k == 0:
collect.append(x)
return collect
print(pincode(k))
You can use a single comprehension for such a task.
k = 6
print([x for x in range(1000, 10000) if x % k == 0])
I think you should try changing the // in if x // k == 0: to % which is an operator that returns the remainder instead of the quotient.
Your function pincode(k) doesn't have a return argument, so it returns none. Append the values to a list, then add that list to the return argument.
k = 6
def pincode(k: int):
a = [] #empty list
for x in range(1000,10000):
if x % k == 0: # use % instead of //
a.append(x) # append x to list
return a #return the list
print(pincode(k))
The double forward slash in Python is known as the integer division operator. Essentially, it will divide the left by the right, and only keep the whole number component.
I would suggest to use % to find if the number is divisible.
k = 6
def pincode(k: int):
for x in range(1000,10000):
#print(f"x and k {x} and {k} res {x%k}")
if x % k == 0:
print(x)
print(pincode(k))

Find first non zero numbers in an array (zeroes can be anywhere)

Suppose we have an array: x = [10,0,30,40]. I would like to extract the first non zero element and store it in a different variable, say y. In this example, y = 10. We can also have many zeros, x = [0,0,30,40], which should give y = 30 as the extracted value.
I tried a Python snippet like this:
i = 0
while x[i] != 0:
y = arr[i]
if x[i] == 0:
break
This only works if the array is [10,0,30,40]. It does not work if I have 0,20,30,40. The loop would stop before that. What is an efficient way to implement this? I try not to use any special Numpy functions, just generic common loops because I might need to port it to other languages.
You can do this
x = [10,0,30,40]
for var in x:
if var != 0:
y = var
break
You could use list comprehension to get all the non-zero values, then provided there are some non-zero values extract the first one.
x = [10,0,30,40]
lst = [v for v in x if v != 0]
if lst:
y = lst[0]
print(y)
The problem with your code is that you don't increment i so it's stuck on the first element. So what you could do to keep the code portable is:
while x[i] != 0:
y = x[i]
if x[i] == 0:
break
i+=1
This code is still not clean, because if there is no 0 in your array then you will get an IndexError as soon as you reach the end.
I'm kind of new to this but i think this should work:
x = [0, 12, 24, 32, 0, 11]
y = []
for num in x:
if num != 0:
y.append(num)
break
print(y)
You can use the next function:
x = [10,0,30,40]
next(n for n in x if n) # 10
x = [0,0,30,40]
next(n for n in x if n) # 30
if you need to support the absence of zero in the list, you can use the second parameter of the next() function:
x = [0,0,0,0]
next((n for n in x if n),0) # 0

map function element-wise where function affects multiple elements

I'm attempting to use 2 rows of an array to add large numbers, using each column of the array as a digit, then going element-wise through each column to determine if a 1 needs to be carried over.
I'm trying to employ a list comprehension along with a map in order to not have to iterate. The issue I'm having is how to handle the syntax such that I can return both the current element and the next higher element (x[y] and x[y+1]):
targdigit = 1000
ind = 2
n = np.zeros((3,targdigit + 1))
def adder(intup):
global targdigit, ind
x = intup[0]
y = intup[1]
ind +=1
if y == targdigit - 1 and x[y] > 0:
print('targ index is ' + str(ind))
sys.exit()
if x[y] >= 10:
x[y] %= 10
x[y+1] += 1
return x
for i in range (2):
n[i][0] = 1
while True:
n[2] = n[0] + n[1]
n[2] = np.array([adder((n[2], xi)) for xi in range(n.shape[1])])
n[0], n[1] = n[1], n[2]
What is the appropriate syntax for the map and the return statement (and perhaps function arguments?) to apply both changes to x[y] and x[y+1]?

TypeError: string indices must be integers..what is the problem

i'm getting TypeError: string indices must be integers... how can i solve this?
And the code is:
def myfunc(x):
y=''
for i in x:
y=y+x[i].upper() +x[i+1]
i+=2
return y
Try to avoid iterating over indices when possible. You can do that there by iterating over x zipped with its own tail:
def myfunc(x):
y = ''
for x1, x2 in zip(x, x[1:]):
y = y + x1.upper() + x2
return y
Even better (and more efficient), you can use join to create y from one list.
import itertools
def myfunc(x):
return ''.join(list(itertools.chain.from_iterable(x1.upper() + x2 for x1,x2 in zip(x, x[1:]))))
You should index x iterating over the actual indices of x, not its values, thus doing range(len(x)), or enumerate(x). Also you cannot modify the iterator in a for loop, you need a while if you want to do so:
def myfunc(x):
y=''
i = 0
while i < len(x) - 1:
y=y+x[i].upper() +x[i+1]
i+=2
return y
When you say: for i in x: is it like you are saying "for each letter in the x string", so at every iteration, i will be the next letter from the string. You can create a simple loop like you have with a print i statement to see the behavior.
Another problem with your code is the x[i + 1] statement. When the for loop will reach the last iteration, you'll try to access something non-existent and you'll get an IndexError. This being said, you should modify your code like this:
def myfunc(x):
y = ''
for i in range(0, len(x) - 1, 2):
y = y + x[i].upper() + x[i + 1]
return y
Because you want to iterate every other character, I also used another set of parameters for range: range([start], stop[, step]).
start: Starting number of the sequence.
stop: Generate numbers up to, but not including this number.
step: Difference between each number in the sequence.
Cheers!
You can do that with enumerate:
Code:
x = 'abcdef'
def myfunc(x):
y = ''
c = 0
for i, j in enumerate(x):
if i == c:
y += j.upper()
else:
y += j
c += 2
return(y)
assert myfunc(x) == 'AbCdEf'
print(myfunc(x))
Output:
AbCdEf

a function that returns number of sums of a certain number.py

I need to write a function that returns the number of ways of reaching a certain number by adding numbers of a list. For example:
print(p([3,5,8,9,11,12,20], 20))
should return:5
The code I wrote is:
def pow(lis):
power = [[]]
for lst in lis:
for po in power:
power = power + [list(po)+[lst]]
return power
def p(lst, n):
counter1 = 0
counter2 = 0
power_list = pow(lst)
print(power_list)
for p in power_list:
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
counter1 == 0
else:
counter1 == 0
return counter2
pow() is a function that returns all of the subsets of the list and p should return the number of ways to reach the number n. I keep getting an output of zero and I don't understand why. I would love to hear your input for this.
Thanks in advance.
There are two typos in your code: counter1 == 0 is a boolean, it does not reset anything.
This version should work:
def p(lst, n):
counter2 = 0
power_list = pow(lst)
for p in power_list:
counter1 = 0 #reset the counter for every new subset
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
return counter2
As tobias_k and Faibbus mentioned, you have a typo: counter1 == 0 instead of counter1 = 0, in two places. The counter1 == 0 produces a boolean object of True or False, but since you don't assign the result of that expression the result gets thrown away. It doesn't raise a SyntaxError, since an expression that isn't assigned is legal Python.
As John Coleman and B. M. mention it's not efficient to create the full powerset and then test each subset to see if it has the correct sum. This approach is ok if the input sequence is small, but it's very slow for even moderately sized sequences, and if you actually create a list containing the subsets rather than using a generator and testing the subsets as they're yielded you'll soon run out of RAM.
B. M.'s first solution is quite efficient since it doesn't produce subsets that are larger than the target sum. (I'm not sure what B. M. is doing with that dict-based solution...).
But we can enhance that approach by sorting the list of sums. That way we can break out of the inner for loop as soon as we detect a sum that's too high. True, we need to sort the sums list on each iteration of the outer for loop, but fortunately Python's TimSort is very efficient, and it's optimized to handle sorting a list that contains sorted sub-sequences, so it's ideal for this application.
def subset_sums(seq, goal):
sums = [0]
for x in seq:
subgoal = goal - x
temp = []
for y in sums:
if y > subgoal:
break
temp.append(y + x)
sums.extend(temp)
sums.sort()
return sum(1 for y in sums if y == goal)
# test
lst = [3, 5, 8, 9, 11, 12, 20]
total = 20
print(subset_sums(lst, total))
lst = range(1, 41)
total = 70
print(subset_sums(lst, total))
output
5
28188
With lst = range(1, 41) and total = 70, this code is around 3 times faster than the B.M. lists version.
A one pass solution with one counter, which minimize additions.
def one_pass_sum(L,target):
sums = [0]
cnt = 0
for x in L:
for y in sums[:]:
z = x+y
if z <= target :
sums.append(z)
if z == target : cnt += 1
return cnt
This way if n=len(L), you make less than 2^n additions against n/2 * 2^n by calculating all the sums.
EDIT :
A more efficient solution, that just counts ways. The idea is to see that if there is k ways to make z-x, there is k more way to do z when x arise.
def enhanced_sum_with_lists(L,target):
cnt=[1]+[0]*target # 1 way to make 0
for x in L:
for z in range(target,x-1,-1): # [target, ..., x+1, x]
cnt[z] += cnt[z-x]
return cnt[target]
But order is important : z must be considered descendant here, to have the good counts (Thanks to PM 2Ring).
This can be very fast (n*target additions) for big lists.
For example :
>>> enhanced_sum_with_lists(range(1,100),2500)
875274644371694133420180815
is obtained in 61 ms. It will take the age of the universe to compute it by the first method.
from itertools import chain, combinations
def powerset_generator(i):
for subset in chain.from_iterable(combinations(i, r) for r in range(len(i)+1)):
yield set(subset)
def count_sum(s, cnt):
return sum(1 for i in powerset_generator(s) if sum(k for k in i) == cnt)
print(count_sum(set([3,5,8,9,11,12,20]), 20))

Categories