Finding the index at which upon removing element string becomes palindrome - python

I'm writing the solution to this HackerRank problem - https://www.hackerrank.com/challenges/palindrome-index
I've tried this code:
T = int(raw_input())
for t in xrange(T):
s = raw_input()
index = -1
if s != s[::-1]:
for i in xrange(len(s)):
temp = s[:i:] + s[i+1::]
if temp == temp[::-1]:
index = i
break
print index
But when I submit it, out of the 14 test cases, around 8 take a long time to compute (~5-7 seconds) and 1 of them takes more than 10 seconds, so HackerRank doesn't even show the result (whether it gave the right output).
It seems my code is inefficient. Please help me out in making it run faster.

The easiest way to speed the code would be to remove slicing for every index in case that string isn't a palindrome. In case of maximum length string which is not a palindrome following line will generate over 200000 slices: temp = s[:i:] + s[i+1::].
You could start checking the string from start and beginning until you spot a difference. Once found you can generate slice which has either first or last letter removed and check if that's a palindrome. In case you removed the first character and result wasn't a palindrome you know that last character is the correct solution since the problem statement guarantees that:
T = int(raw_input())
for t in xrange(T):
s = raw_input()
length = len(s)
for i in xrange(length / 2):
if s[i] != s[length - i - 1]:
if s[i + 1:length - i] == s[length - i - 1:i:-1]:
print i
else:
print length - i - 1
break
else:
print -1

The most efficient way would be to check from both sides left and right and break on inequality:
for i in range(int(input())):
s=input()
if s==s[::-1]:
print(-1)
else:
for i in range(int(len(s)/2)):
if s[i]!=s[len(s)-1-i]:
print(i if ((s[:i]+s[i+1:])==(s[:i]+s[i+1:])[::-1]) else len(s)-1-i)
break
Apparently I'm a member of that site as well, executed my code and it passes all test cases with 0.01 s and 0.02s

Related

Receiving exit code (1) when trying to manipulate strings from codewars assignment? How can I do it differently and avoid Index Errors?

I have been working on this codewars assignment the entire day, but I keep failing the test on code wars. Running into index errors and exit code (1) even though I am passing every test and attempt, the exit code seems so illusive yet confusing. I understand if my program doesn't do what it is supposed to but it is passing the tests, and right now there are some errors I don't quite understand messing up my program in ways that I can't even comprehend. Because they are returning the strings exactly as requested by the assignment, so why... is it not working? What have I done wrong? Please help me almighty code wizards, I am a noob...
Working on Split Strings: Complete the solution so that it splits the string into pairs of two characters. If the string contains an odd number of characters then it should replace the missing second character of the final pair with an underscore ('_').
Here is my Code:
def solution(string):
join_string = "-".join(string[i:i+2] for i in range(0, len(string), 2))
x_string = join_string.split("-")
underscore = "_"
index = (len(string) - len(x_string))
if index % 2 == 0:
for i in range(0, index):
return x_string
break
else:
x_string[index] = x_string[index] + underscore
return x_string
elif index > 2:
x_string[index] = x_string[index] + underscore
return x_string
elif index < 1:
x_string = []
return x_string
The error comes up at
x_string[index] = x_string[index] + underscore
IndexError: list index out of range
This line
join_string = "-".join(string[i:i+2] for i in range(0, len(string), 2))
already does the entire job you want, except for the underscore at the end if there were an odd number of characters in the input. Your code is making very heavy weather of this special case. Check if the input string has an odd number of characters:
if len(string) % 2 == 1:
and if it has, append an underscore to the returned value:
join_string += "_"
Your question doesn't say what the expected output is, and it's hard to tell from the code, so, to keep to the essential point, the whole function becomes
def solution(string):
join_string = "-".join(string[i:i+2] for i in range(0, len(string), 2))
if len(string) % 2 == 1:
join_string += "_"
return join_string
I get the output
>>> solution("123465")
'12-34-65'
>>> solution("1234657")
'12-34-65-7_'
>>> solution("1")
'1_'
>>> solution("")
''
If instead you want, say, a list of 2-strings, then do
return join_string.split("-")

How To Fix String Index Out of Range in Python

I'm currently learning python. There is an exercise in the book which outlines code for decoding. I followed along with this code however I keep getting back an error in the middle of the program.
Here is the code that is causing the problem:
def decode(string_for_decoding):
result = ""
for i in range(0, len(string_for_decoding)):
if string_for_decoding[i+1].isdigit():
result += string_for_decoding[i] * int(string_for_decoding[i+1])
elif string_for_decoding[i].isalpha():
result += string_for_decoding[i]
return result
string_for_decoding = input("Enter a string to decode: ")
print(decode(string_for_decoding))
Check if the index from range is larger than the number of chars in the string. It might look like this:
def decode(string_for_decoding: str):
result = ""
for i in range(0, len(string_for_decoding)):
if len(string_for_decoding) > i + 1 and string_for_decoding[i + 1].isdigit():
result += string_for_decoding[i] * int(string_for_decoding[i + 1])
elif string_for_decoding.isalpha():
result += string_for_decoding[i]
return result
print(decode(input("Enter a string to decode: ")))
You are going from 0 to len(string) and inside for loop you are trying to access index: i+1
THis is the root cause! Either:
iterate till one length less e.g. till len(string) - 1
Or use indices
inside appropriately
Moreover it is highly discouraged to use range(len(x)). You can simply iterate over any collective datatype as for i in x: If you want indices too have a look at enumerate(). And you can forget about index errors all together.
You are trying to get index i+1 which cannot be equal or greater than len(string).

Round G - Kick Start 2020 Problem Kick_Start Wrong Answer

Problem:
Ksenia is very fond of reading so she kicks off each day by reading a
fragment from her favourite book before starting with the rest of her
morning routine. A fragment is simply a substring of the text. Ksenia
is somewhat superstitious and believes that her day will be lucky if
the fragment she reads starts with the string KICK, then goes on with
0 or more characters, and eventually ends with the string START, even
if the overall fragment makes little sense.
Given the text of the book, count the number of different lucky
fragments that Ksenia can read before the book gets old and she needs
to buy another one. Two fragments are considered to be different if
they start or end at different positions in the text, even if the
fragments read the same. Also note that different lucky fragments may
overlap.
Input:
The first line of the input gives the number of test cases T. T lines
follow, each containing a single string S consisting of upper case
English letters only.
Output:
For each test case, output one line containing Case #x: y, where x is
the test case number (starting from 1) and y is the number of
different lucky fragments in the text of this test case.
Limits:
Memory limit: 1 GB. 1 ≤ T ≤ 100. S consists of upper-case English
letters only.
Test Set 1:
Time limit: 20 seconds.
1 ≤ |S| ≤ 1000.
Test Set 2:
Time limit: 40 seconds.
1 ≤ |S| ≤ 105.
Sample:
Input
3
AKICKSTARTPROBLEMNAMEDKICKSTART
STARTUNLUCKYKICK
KICKXKICKXSTARTXKICKXSTART
Output
Case #1: 3
Case #2: 0
Case #3: 5
I tried solving it using Python. The logic I tried using is to find indices for substring 'KICK' and substring 'START' and find number of START appearing after every 'KICK'.
I'm getting wrong answer I don't understand what edge cases I'm missing.
Here is the code:
import re
t = int(input())
for i in range(t):
text = input()
matches = 0
temp1 = [m.start() for m in re.finditer('KICK',text)]
temp2 = [m.start() for m in re.finditer('START',text)]
if len(temp1) == 0 or len(temp2) == 0:
matches = 0
else:
for ele in temp1:
for x in temp2:
if(x > ele):
matches = matches + 1
print("Case "+"#"+str(i+1)+": "+str(matches))
Finally I found the cases the code misses.
re.finditer() can only count non-overlapping matches and hence strings like 'KICKICK' would result in counting KICK only for 1 time rather than 2.
re.finditer(pattern, string, flags=0)
Return an iterator yielding MatchObject instances over all non-overlapping matches for the RE pattern in string.The string is scanned left-to-right, and matches are returned in the order found. Empty matches are included in the result.
Here is my code:
t = int(input())
for x in range(t):
s = input()
ans = 0
k = 0
for i in range(len(s)-4):
if s[i:i+4] == "KICK":
k += 1
if s[i:i+5] == "START" and k != 0:
ans += k
print("Case #{}: {}".format(x+1, ans))
You are not considering all the possibilities. The code just shows how many time the KICK and START occurs
I Hope this works.
T=int(input())
for x in range(1, T + 1):
P=input()
c=0
for i in range(0,len(P)-4+1):
if (P[i:i+4]=="KICK"):
for j in range(i+4,len(P)-5+1):
if (P[j:j+5]=="START"):
c=c+1
print("Case #{}: {}".format(x, c), flush = True)

To Remove consecutive vowels from string

Here I want to remove any vowels which repeat consecutively. But it shows error as "list out of index".
So I tried break if last element in list is reached, but still won't work.
Here is the code I tried:-
a=[]
b=str(input("enter the string"))
a=b.split(',')
c=['a','e','i','o','u']
for i in c:
for j in range(0,len(a)):
if (a[j+1] == a[len(a)]) is True:
break;
elif ((a[j] == a[j+1]) & (a[j+1] == i)) is True:
del[j]
e.join(a)
print(e)
Please show me how to solve this problem, or any other problem if in there.
How about maintaining a stack for consecutive vowels? whenever you see non-vowel string re-initialize the stack list and when you see vowels but are not consecutive you just add to the final list
stack=[]
new_list=[]
vowel=['a','i','o','u','e']
for i in your_string: # replace your string with actual string
if i not in vowel:
if len(stack) == 1:
new_list.append(stack[0])
new_list.append(i)
stack = []
else:
stack.append(i)
if len(stack) == 1:
new_list.append(stack[0])
You get a list out of index error because you are indexing at a value not in a
if (a[j+1] == a[len(a)]) is True:
a[len(a)] does not exist, arrays are zero indexed so they start from 0. An array with 5 items has index 0,1,2,3,4
the line should be:
if (a[j+1] == a[len(a) - 1]) is True:
Also 'is True' is redundant, so further refined:
if a[j+1] == a[len(a) - 1]:
Also is del[j] an error? should it be del a[j]?
If this is the case, the program will run into further errors as you are iterating over the entire Original size of the array but deleting values during this iteration, so it will look for items that no longer exist.
First detail is this: if (a[j+1] == a[len(a)]) is True: from what I understood this is to break the code when it is necessary. But that is completely unnecessary. Instead you should fix the number of iterations in the beginning, which should be for j in range(0,len(a)-1):
Another problem is that you aren't really iterating through the letters, just each comma separated phrases. If you put "Hello, World" you aren't checking the letters, you check "Hello" and " World". You might as well remove b and let a just be the raw input.
Since a would then be a string, to erase the i-th letter of a string you can use the function below.
def remove_jth(word, j):
word = word[:j] + word[j+1:]
Finally since you are using range, it will build a "list" that has the size len(a) when you start the for, but as you remove vowels, the length of a gets shorter and the range you had at the beginning will be to much. If you fix all of these things you should have it right
You can do it this way:
a=[]
b=str("a,a,a,b,e,e,e,c,x,d")
a=b.split(',')
c=['a','e','i','o','u']
j = 0
for i in c:
while j < len(a)-1:
if a[j] == a[j+1] and a[j] == i:
del a[j]
else:
j = j+1
j=0
Use a while loop to check through the list and delete consecutive duplicates. Then reset the the while loop back to zero and run through the list again.
Using a for loop will not work,if you are deleting items in the list that is being used to iterate over.
b=str("a,a,a,b,e,e,e,c,x,d") was just used to test the code.
To print the results you can just do print(a) it will print the list out.
I used this Stack Overflow post as reference.
I edited it to suit you:
result=original.replace('a','').replace('e',''),replace('i','').replace('o','').replace('u','')
original is your input string and result is your output string.
t=input()
st=""
vow=['a','e','i','o','u']
st+=t[0]
for i in range(1,len(t)):
if(t[i].lower() in vow):
if(t[i-1].lower() not in vow and i-1>=0):
st+=t[i]
else:
st+=t[i]
print(st)

counting the number of palindromes in a range in python

I'm sort of new to python. I'm trying to go through a specific range of numbers and have python count all the palindromes in it and return them to me (total number count, not their sum). So it would count all the integers in this range and return it to me as one number.
I keep getting an invalid syntax error and I don't know what to change. Here is what I have so far:
import math
def ispal(n):
return str(n) == str(n)[::-1]
But this is basically just what we did in class.
My range of of numbers is from 171 to 115000 and I want to go through the entire range in between and including those 2 numbers and have python tell me how many numbers are palindromes. The problem is I don't know how to fit in the for loop.
I started with:
def count_pal(n):
count = 0
for i in range(n):
if i = str(n) == str(n)[::-1]:
return:
count =+ i
else:
pass
But I don't know how to put the 2 together. I have python 3.2. Can anyone please help me out? Thank you!
def num_palindromes(start, end):
count = 0
for i in range(start, end + 1):
if str(i) == str(i)[::-1]:
count += 1
return count
Or as a one liner
def num_palindromes(start, end):
return sum(str(i) == str(i)[::-1] for i in range(start, end + 1))
You're returning inside the for loop before you have a chance to increment the counter
You also don't need that empty 'else: pass' block as it does nothing.
A correct solution will return the counter at the end of the function after the loop terminates.
Something like this will work:
count = 0
for i in range(171, 115000):
if str(i) == str(i)[::-1]:
count += 1
return count
Note a few style changes:
- 4-space indentation
- no extraneous newlines
- no unnecessary coercion of i from 'True/False' to a number (which is what you get in your code when you do i = str(i) == str(i)[::-1])
Not directly related to your question but following python conventional style will help make your code more readable and easier for others to understand and help you with.
Lastly, just as n extra tidbit, you can also accomplish this task with a list comprehension:
sum([1 for i in range(171, 115000) if str(i) == str(i)[::-1]])
I personally find it more concise/easier to understand than the loop counter variation.

Categories

Resources