How to retrieve a unique character from a string using while loop - python

I was tasked with creating a function that looks for the unique char in a string and from there add the value of that to 10. For this specific scenario dice_str will always have a len of 3, and there will always be a unique char within that str. This is my code so far:
def get_point_score(dice_str):
unique = ""
i = 0
while len(unique) != 1:
if dice_str[i] not in dice_str[i+1:]:
unique += dice_str[i]
sum = 10 + int(unique)
else:
i += 1
unique += unique
return sum
Expected output:
dice_str = "141"
value = 14
Got:
value = 11
We were asked to solve this using while loops, indexing and find().
I was thinking of iterating through every element in the string and using indexing to see if it is repeated, and if it is repeated to increment i by 1 and continue checking until this is no longer the case. From the output it looks like I have failed to implement this properly, so then I was thinking of using find() and once find() returns -1, add the element to the empty string, but I also am not sure how to implement that properly. Any help would be appreciated

Your problem is your check if dice_str[i] not in dice_str[i + 1:]:.
Take note of this example "116"
On your first time through you will check if 1 is in 16. Which it is.
Then you check if 1 is in 6 which it is not. But then you perform this operation:
unique += dice_str[i]
However in this case, your unique value is 6 (index i + 1) not 1 (index i).
You were really close in your implementation. Instead of testing if a value is not in the next slice up, you can build a list that excludes the current index, and test against that instead:
def get_point_score(dice_str):
i = 0
unique = None
while not unique:
if dice_str[i] not in (dice_str[:i] + dice_str[i + 1:]):
unique = dice_str[i]
i += 1
return 10 + int(unique)
*Warning this will raise an error if your list does not contain at least one unique value.
Given that you're guaranteed a length 3 string with a single unique character, you can just do left/right comparison.
def get_point_score(dice_str):
left_match = dice_str[0] == dice_str[1] # Do 0 and 1 Match?
right_match = dice_str[1] == dice_str[2] # Do 1 and 2 Match?
if not left_match and not right_match:
# Neither Match (Middle Unique)
unique = dice_str[1]
elif left_match:
# 0 and 1 Match, but 1 and 2 don't (Right Unique)
unique = dice_str[2]
else:
# 0 and 1 Don't Match, but 1 and 2 do (Left Unique)
unique = dice_str[0]
return 10 + int(unique)
print(get_point_score("116"))
print(get_point_score("141"))
print(get_point_score("722"))
Output:
16
14
17

Related

How to count the number of triplets found in a string?

string1 = "abbbcccd"
string2 = "abbbbdccc"
How do I find the number of triplets found in a string. Triplet meaning a character that appears 3 times in a row. Triplets can also overlap for example in string 2 (abbbbdccc)
The output should be:
2 < -- string 1
3 <-- string 2
Im new to python and stack overflow so any help or advice in question writing would be much appreciated.
Try iterating through the string with a while loop, and comparing if the character and the two other characters in front of that character are the same. This works for overlap as well.
string1 = "abbbcccd"
string2 = "abbbbdccc"
string3 = "abbbbddddddccc"
def triplet_count(string):
it = 0 # iterator of string
cnt = 0 # count of number of triplets
while it < len(string) - 2:
if string[it] == string[it + 1] == string[it + 2]:
cnt += 1
it += 1
return cnt
print(triplet_count(string1)) # returns 2
print(triplet_count(string2)) # returns 3
print(triplet_count(string3)) # returns 7
This simple script should work...
my_string = "aaabbcccddddd"
# Some required variables
old_char = None
two_in_a_row = False
triplet_count = 0
# Iterates through characters of a given string
for char in my_string:
# Checks if previous character matches current character
if old_char == char:
# Checks if there already has been two in a row (and hence now a triplet)
if two_in_a_row:
triplet_count += 1
two_in_a_row = True
# Resets the two_in_a_row boolean variable if there's a non-match.
else:
two_in_a_row = False
old_char = char
print(triplet_count) # prints 5 for the example my_string I've given

why Im having String indexing problem in Python

I'm trying to understand why I'm having the same index again when I apply .index or .find
why I'm getting the same index '2' again why not '3'? when a letter is repeated, and what is the alternative way to get an index 3 for the second 'l'
text = 'Hello'
for i in text:
print(text.index(i))
the output is:
0
1
2
2
4
It's because .index() returns the lowest or first index of the substring within the string. Since the first occurrence of l in hello is at index 2, you'll always get 2 for "hello".index("l").
So when you're iterating through the characters of hello, you get 2 twice and never 3 (for the second l). Expanded into separate lines, it looks like this:
"hello".index("h") # = 0
"hello".index("e") # = 1
"hello".index("l") # = 2
"hello".index("l") # = 2
"hello".index("o") # = 4
Edit: Alternative way to get all indices:
One way to print all the indices (although not sure how useful this is since it just prints consecutive numbers) is to remove the character you just read from the string:
removed = 0
string = "hello world" # original string
for char in string:
print("{} at index {}".format(char, string.index(char) + removed)) # index is index() + how many chars we've removed
string = string[1:] # remove the char we just read
removed +=1 # increment removed count
text = 'Hello'
for idx, ch in enumerate(text):
print(f'char {ch} at index {idx}')
output
char H at index 0
char e at index 1
char l at index 2
char l at index 3
char o at index 4
If you want to find the second occurance, you should search in the substring after the first occurance
text = 'Hello'
first_index = text.index('l')
print('First index:', first_index)
second_index = text.index('l', first_index+1) # Search in the substring after the first occurance
print('Second index:', second_index)
The output is:
First index: 2
Second index: 3

Subtracting substring from string in as many possible steps

Goal is to find the maximum amount of times you can subtract t from s.
t = ab, s = aabb. In the first step, we check if t is contained within s. Here, t is contained in the middle i.e. a(ab)b. So, we will remove it and the resultant will be ab and increment the count value by 1. We again check if t is contained within s. Now, t is equal to s i.e. (ab). So, we remove that from s and increment the count. So, since t is no more contained in s, we stop and print the count value, which is 2 in this case.
Problem occurs when you have something as s = 'abbabbaa' t = 'abba'.
Now it matters if you take it from the end or beggining, since you will get more steps from the end.
def MaxNum(s,t):
if not t in s:
return 0
elif s.count(t) == 1:
front = s.find(t)
sfront = s[:front] + s[front + len(t):]
return 1 + MaxNum(sfront,t)
else:
back = s.rfind(t)
front = s.find(t)
sback = s[:back] + s[back +len(t):]
sfront = s[:front] + s[front + len(t):]
print (sfront,sback)
return max(1 + MaxNum(sfront,t),1 + MaxNum(sback,t))
def foo(t,s):
return max([0] + [
1 + foo(t,s[:i]+s[i+len(t):]) for i in range(len(s)) if s[i:].startswith(t)])
Should I ask why you care?

Optimize python code to avoid runtime error

Given a string that might have multiple occurrences of the same character, return the closest same character of any indicated character in the string.
Given the string s and n number of queries. In each query, you are given an index a (where 0 <= a <= |s| ) of a character, and you need to print the index of the closet same character. If there are multiple answers, print the smallest one. Otherwise, print -1.
For example, string s = 'youyouy', with a given query 3: there are two matching character at indices 0 and 6, each 3 away, we choose the smallest one which is 0.
Here is my plan:
I put the string in a dictionary, the key is distinct letters in a string, values are letters corresponding indexes. When given a query, find the corresponding letter in the dictionary and return the closest value to the query.
def closest(s, queries):
res = []
dict2={}
#dict2 - letter - indexs
for i in range(len(s)):
if s[i] not in dict2:
dict2[s[i]]=[i]
else:
dict2[s[i]].append(i)
for num in queries:
#closet- denotes closet letter index
closet = math.inf
#num is out of range , append -1
if num > (len(s)-1):
res.append(-1)
continue
#this is the only one letter, append -1
letter=s[num]
if len(dict2[letter])==1:
res.append(-1)
continue
#temp = list for that letters
temp=dict2[s[num]]
index=temp.index(num) . #in the list, letter index's index in list
if index==0:
closet=temp[1]
elif index==(len(temp)-1):
closet=temp[index-1]
else:
distance1=num-temp[index-1] . #left
distance2=temp[index+1]-num . #right
if distance1 <= distance2:
closet=temp[index-1]
else:
closet=temp[index+1]
if closet == math.inf:
res.append(-1)
else:
res.append(closet)
return res
I got two runtime error. I am wondering if you could help me out to maybe reduce some run time ?
Also, I am looking for another suggestions! I have used Python for a while, and I am looking for a job (university new grad). Is java usually running faster than Python? Should I switch to Java?
Im trying to do as simple as i can , but i look like a bit complex. Though you question is avoiding runtime error , i want to present my idea
s='oooyyouoy'
k='0123456789'
def cloest(string,pos):
c = string[pos]
p1 , p2 = s[:pos] , s[pos+1:]
# reserve left part and find the closet one , add 1 because len(p1)=final_position + 1
l = len(p1) - (p1[::-1].find(c) + 1)
# find without reserve and add 1 because s[pos+1:]
r = (p2.find(c) + 1) + pos
# judge which one is closer if same chose left one
result = l if (pos - l) <= (r - pos) else r
if result == pos:
return -1
else:
return result
print(cloest(s,4))

Infinite string

We are given N words, each of length at max 50.All words consist of small case alphabets and digits and then we concatenate all the N words to form a bigger string A.An infinite string S is built by performing infinite steps on A recursively: In ith step, A is concatenated with ′$′ i times followed by reverse of A. Eg: let N be 3 and each word be '1','2' and '3' after concatenating we get A= 123 reverse of a is 321 and on first recursion it will be
A=123$321 on second recursion it will be A=123$321$$123$321 And so on… The infinite string thus obtained is S.Now after ith recursion we have to find the character at index say k.Now recursion can be large as pow(10,4) and N which can be large as (pow(10,4)) and length of each word at max is 50 so in worst case scenario our starting string can have a length of 5*(10**5) which is huge so recursion and adding the string won't work.
What I came up with is that the string would be a palindrome after 1 st recursion so if I can calculate the pos of '$'*I I can calculate any index since the string before and after it is a palindrome.I came up with a pattern that
looks like this:
string='123'
k=len(string)
recursion=100
lis=[]
for i in range(1,recursion+1):
x=(2**(i-1))
y=x*(k+1)+(x-i)
lis.append(y)
print(lis[:10])
Output:
[4, 8, 17, 36, 75, 154, 313, 632, 1271, 2550]
Now I have two problems with it first I also want to add position of adjacent '$' in the list because at position 8 which is the after 2nd recursion there will be more (recursion-1)=1 more '$' at position 9 and likewise for position 17 which is 3rd recursion there will be (3-1) two more '$' in position 18 and 19 and this would continue until ith recursion and for that I would have to insert while loop and that would make my algorithm to give TLE
string='123'
k=len(string)
recursion=100
lis=[]
for i in range(1,recursion+1):
x=(2**(i-1))
y=x*(k+1)+(x-i)
lis.append(y)
count=1
while(count<i):
y=y+1
lis.append(y)
count+=1
print(lis[:10])
Output: [4, 8, 9, 17, 18, 19, 36, 37, 38, 39]
The idea behind finding the position of $ is that the string before and after it is a palindrome and if the index of $ is odd the element before and after it would be the last element of the string and it is even the element before and after it would be the first element of the string.
The number of dollar signs that S will have in each group of them follows the following sequence:
1 2 1 3 1 2 1 4 1 2 1 ...
This corresponds to the number of trailing zeroes that i has in its binary representation, plus one:
bin(i) | dollar signs
--------+-------------
00001 | 1
00010 | 2
00011 | 1
00100 | 3
00101 | 1
00110 | 2
... ...
With that information you can use a loop that subtracts from k the size of the original words and then subtracts the number of dollars according to the above observation. This way you can detect whether k points at a dollar or within a word.
Once k has been "normalised" to an index within the limits of the original total words length, there only remains a check to see whether the characters are in their normal order or reversed. This depends on the number of iterations done in the above loop, and corresponds to i, i.e. whether it is odd or even.
This leads to this code:
def getCharAt(words, k):
size = sum([len(word) for word in words]) # sum up the word sizes
i = 0
while k >= size:
i += 1
# Determine number of dollars: corresponds to one more than the
# number of trailing zeroes in the binary representation of i
b = bin(i)
dollars = len(b) - b.rindex("1")
k -= size + dollars
if k < 0:
return '$'
if i%2: # if i is odd, then look in reversed order
k = size - 1 - k
# Get the character at the k-th index
for word in words:
if k < len(word):
return word[k]
k -= len(word)
You would call it like so:
print (getCharAt(['1','2','3'], 13)) # outputs 3
Generator Version
When you need to request multiple characters like that, it might be more interesting to create a generator, which just keeps producing the next character as long as you keep iterating:
def getCharacters(words):
i = 0
while True:
i += 1
if i%2:
for word in words:
yield from word
else:
for word in reversed(words):
yield from reversed(word)
b = bin(i)
dollars = len(b) - b.rindex("1")
yield from "$" * dollars
If for instance you want the first 80 characters from the infinite string that would be built from "a", "b" and "cd", then call it like this:
import itertools
print ("".join(itertools.islice(getCharacters(['a', 'b', 'cd']), 80)))
Output:
abcd$dcba$$abcd$dcba$$$abcd$dcba$$abcd$dcba$$$$abcd$dcba$$abcd$dcba$$$abcd$dcba$
Here is my solution to the problem (index starts at 1 for findIndex) I am basically counting recursively to find the value of the findIndex element.
def findInd(k,n,findIndex,orientation):
temp = k # no. of characters covered.
tempRec = n # no. of dollars to be added
bool = True # keeps track of if dollar or reverse of string is to be added.
while temp < findIndex:
if bool:
temp += tempRec
tempRec += 1
bool = not bool
else:
temp += temp - (tempRec - 1)
bool = not bool
# print(temp,findIndex)
if bool:
if findIndex <= k:
if orientation: # checks if string must be reversed.
return A[findIndex - 1]
else:
return A[::-1][findIndex - 1] # the string reverses when there is a single dollar so this is necessary
else:
if tempRec-1 == 1:
return findInd(k,1,findIndex - (temp+tempRec-1)/2,False) # we send a false for orientation as we want a reverse in case we encounter a single dollar sign.
else:
return findInd(k,1,findIndex - (temp+tempRec-1)/2,True)
else:
return "$"
A = "123" # change to suit your need
findIndex = 24 # the index to be found # change to suit your need
k = len(A) # length of the string.
print(findInd(k,1,findIndex,True))
I think this will satisfy your time constraint also as I do not go through each element.

Categories