Hello I am fairly new at programming,
I would like to know is there a function or a method that allows us to find out how many letters have been changed in a string..
example:
input:
"Cold"
output:
"Hold"
Hence only 1 letter was changed
or the example:
input:
"Deer"
output:
"Dial"
Hence 3 letters were changed
I spoke too soon. First result googling:
https://pypi.python.org/pypi/python-Levenshtein/
This should be able to measure the minimum number of changes needed to get from one string to another.
If you don't need to consider character insertions or deletions, the problem is reduced to simply counting the number of characters that are different between the strings.
Since you're new to programming, a imperative-style program would be:
def differences(string1,string2):
i=0
different=0
for i in range(len(string1)):
if string1[i]!=string2[i]:
different= different+1
return different
something slightly more pythonic would be:
def differences(string1,string2):
different=0
for a,b in zip(string1,string2):
if a!=b:
different+= 1
return different
or, if you want to go fully functional:
def differences(string1,string2):
return sum(map(lambda (x,y):x!=y, zip(string1,string2)))
which, as #DSM suggested, is equivalent to the more readable generator expression:
def differences(string1,string2):
return sum(x != y for x,y in zip(string1, string2))
Use the itertools library as follows (Python 3.x)
from itertools import zip_longest
def change_count(string1, string2):
count = 0
for i, (char1, char2) in enumerate(zip_longest(string1, string2)):
if char1 != char2:
count = count + 1
return count
string1 = input("Enter one string: ")
string2 = input("Enter another string: ")
changed = change_count(string1, string2)
print("Times changed: ", changed)
Check out the difflib library, particularly then ndiff method. Note: this is kind of overkill for the required job, but it is really great for seeing the differences between two files (you can see which are new, which are changed, etc etc)
word1 = "Cold"
word2 = "Waldo"
i = 0
differences = difflib.ndiff(word1, word2)
for line in differences:
if line[0] is not " ":
i += 1
print(i)
Related
I am trying to solve this problem on HackerRank and I am having a issue with my logic. I am confused and not able to think what I'm doing wrong, feels like I'm stuck in logic.
Question link: https://www.hackerrank.com/challenges/game-of-thrones/
I created a dictionary of alphabets with value 0. And then counting number of times the alphabet appears in the string. If there are more than 1 alphabet characters occurring 1 times in string, then obviously that string cannot become a palindrome. That's my logic, however it only pass 10/21 test cases.
Here's my code:
def gameOfThrones(s):
alpha_dict = {chr(x): 0 for x in range(97,123)}
counter = 0
for i in s:
if i in alpha_dict:
alpha_dict[i] += 1
for key in alpha_dict.values():
if key == 1:
counter += 1
if counter <= 1:
return 'YES'
else:
return 'NO'
Any idea where I'm going wrong?
Explanation
The issue is that the code doesn't really look for palindromes. Let's step through it with a sample text based on a valid one that they gave: aaabbbbb (the only difference between this and their example is that there is an extra b).
Your first for loop counts how many times the letters appear in the string. In this case, 3 a and 5 b with all the other characters showing up 0 times (quick aside, the end of the range function is exclusive so this would not count any z characters that might show up).
The next for loop counts how many character there are that show up only once in the string. This string is made up of multiple a and b characters, more than the check that you have for if key == 1 so it doesn't trigger it. Since the count is less than 1, it returns YES and exits. However aaabbbbb is not a palindrome unscrambled.
Suggestion
To fix it, I would suggest having more than just one function so you can break down exactly what you need. For example, you can have a function that would return a list of all the unscrambled possibilities.
def allUnscrambled(string)->list:
# find all possible iterations of the string
# if given 'aabb', return 'aabb', 'abab', 'abba', 'bbaa', 'baba', 'baab'
return lstOfStrings
After this, create a palindrome checker. You can use the one shown by Dmitriy or create your own.
def checkIfPalindrome(string)->bool:
# determine if the given string is a palindrome
return isOrNotPalindrome
Put the two together to get a function that will, given a list of strings, determine if at least one of them is a palindrome. If it is, that means the original string is an anagrammed palindrome.
def palindromeInList(lst)->bool:
# given the list of strings from allUnscrambled(str), is any of them a palindrome?
return isPalindromeInList
Your function gameOfThrones(s) can then call this palindromeInList( allUnscrambled(s) ) and then return YES or NO depending on the result. Breaking it up into smaller pieces and delegating tasks is usually a good way to handle these problems.
Corrected the logic in my solution. I was just comparing key == 1 and not with every odd element.
So the corrected code looks like:
for key in alpha_dict.values():
if key % 2 == 1:
counter += 1
It passes all the testcases on HackerRank website.
The property that you have to check on the input string is that the number of characters with odd repetitions must be less than 1. So, the main ingredients to cook you recipe are:
a counter for each character
an hash map to store the counters, having the characters as keys
iterate over the input string
A plain implementation could be:
def gameOfThrones(s):
counters = {}
for c in s:
counters[c] = counters.get(c, 0) + 1
n_odd_characters = sum(v % 2 for v in counters.values())
Using a functional approach, based on reduce from functools:
from functools import reduce
def gamesOfThrones(s):
return ['NO', 'YES'][len(reduce(
lambda x, y: (x | {y: 1}) if y not in x else (x.pop(y) and x),
s,
{}
)) <= 1]
If you want, you can use the Counter class from collections to make your code more concise:
def gamesOfThrones(s):
return ['NO', 'YES'][sum([v % 2 for v in Counter(s).values() ]) <= 1]
I am trying to find occurrence of letter 'b' and 'B'. the code that I have written works perfectly. Is there a better way that i can do this.
My code:
def count_letter_b(string):
#TODO: Your code goes here
a = int(string.count('B'))
b = int(string.count('b'))
return a + b
print count_letter_b("Bubble Bungle")
You can turn the string to uppercase (or lowercase), then count the occurrences:
string.upper().count('B')
So, overall, your code will look like this:
def count_letter_b(string):
return string.upper().count('B')
Note: no need to cast to int(..) as the result of str.count is already an int
Well if you only want to apply the same computation to a varying amount of letters you may want them to be arguments (count_letter(s, letters)), but anyway, here is a more functional example:
def count_letter_b(string):
return sum(map(string.count, 'Bb'))
This uses the str.count version that is bound to your input string instance.
Note that you're shadowing the name string if you use it as a parameter name.
You could do
# count in upper string, upper character
def countInvariantChars(c,s):
return s.upper().count(c.upper())
# list comprehensions + length
def countInvariantChars2(c,s):
return len([x for x in s if c.upper() == x.upper()])
# sum ones of list comprehension
def countInvariantChars3(c,s):
return sum([1 for x in s if c.upper() == x.upper()])
print(countInvariantChars("b","Bubble Bungle"))
print(countInvariantChars2("b","Bubble Bungle"))
print(countInvariantChars3("b","Bubble Bungle"))
Output (pyfiddle.io):
read-only#bash: 4
4
4
Use this:
def count_letter_b(string):
return string.lower().count('b')
print(count_letter_b(string))
[Edit: as someone pointed out I have used improperly the palindrom concept, now I have edited with the correct functions. I have done also some optimizations in the first and third example, in which the for statement goes until it reach half of the string]
I have coded three different versions for a method which checks if a string is a palindrome. The method are implemented as extensions for the class "str"
The methods also convert the string to lowercase, and delete all the punctual and spaces. Which one is the better (faster, pythonic)?
Here are the methods:
1) This one is the first solution that I thought of:
def palindrom(self):
lowerself = re.sub("[ ,.;:?!]", "", self.lower())
n = len(lowerself)
for i in range(n//2):
if lowerself[i] != lowerself[n-(i+1)]:
return False
return True
I think that this one is the more faster because there aren't transformations or reversing of the string, and the for statement breaks at the first different element, but I don't think it's an elegant and pythonic way to do so
2) In the second version I do a transformation with the solution founded here on stackoverflow (using advanced slicing string[::-1])
# more compact
def pythonicPalindrom(self):
lowerself = re.sub("[ ,.;:?!]", "", self.lower())
lowerReversed = lowerself[::-1]
if lowerself == lowerReversed:
return True
else:
return False
But I think that the slicing and the comparision between the strings make this solution slower.
3) The thirds solution that I thought of, use an iterator:
# with iterator
def iteratorPalindrom(self):
lowerself = re.sub("[ ,.;:?!]", "", self.lower())
iteratorReverse = reversed(lowerself)
for char in lowerself[0:len(lowerself)//2]:
if next(iteratorReverse) != char:
return False
return True
which I think is way more elegant of the first solution, and more efficient of the second solution
So, I decided to just timeit, and find which one was the fastest. Note that the final function is a cleaner version of your own pythonicPalindrome. It is defined as follows:
def palindrome(s, o):
return re.sub("[ ,.;:?!]", "", s.lower()) == re.sub("[ ,.;:?!]", "", o.lower())[::-1]
Methodology
I ran 10 distinct tests per function. In each test run, the function was called 10000 times, with arguments self="aabccccccbaa", other="aabccccccbaa". The results can be found below.
palindrom iteratorPalindrome pythonicPalindrome palindrome
1 0.131656638 0.108762937 0.071676536 0.072031984
2 0.140950052 0.109713793 0.073781851 0.071860462
3 0.126966087 0.109586756 0.072349792 0.073776719
4 0.125113136 0.108729573 0.094633969 0.071474645
5 0.130878159 0.108602964 0.075770395 0.072455015
6 0.133569472 0.110276694 0.072811747 0.071764222
7 0.128642812 0.111065438 0.072170571 0.072285204
8 0.124896702 0.110218949 0.071898959 0.071841214
9 0.123841905 0.109278358 0.077430437 0.071747112
10 0.124083576 0.108184210 0.080211147 0.077391086
AVG 0.129059854 0.109441967 0.076273540 0.072662766
STDDEV 0.005387429 0.000901370 0.007030835 0.001781309
It would appear that the cleaner version of your pythonicPalindrome is marginally faster, but both functions clearly outclass the alternatives.
It seems that you want to know the execution time of your blocks of code and compare them.
You can use the timeit module.
Here's a quick way:
import timeit
start = timeit.default_timer()
#Your code here
stop = timeit.default_timer()
print stop - start
Read more:
Option 1
Option 2
You could also time this one-liner that does not use re, but itertools instead:
def isPalindrom(self):
return all(i==j for i, j in itertools.zip_longest((i.lower() for i in self if i not in " ,.;:?!"), (j.lower() for j in self[::-1] if j not in " ,.;:?!")))
Or, explained in more details:
def isPalindrom(self):
#using generators to not use memory
stripped_self = (i.lower() for i in self if i not in " ,.;:?!")
reversed_stripped_self = (j.lower() for j in self[::-1] if j not in " ,.;:?!")
return all(self_char==reversed_char for self_char, reversed_char in itertools.zip_longest(stripped_self, reversed_stripped_self))
Recall that filter works on strings:
>>> st="One string, with punc. That also needs lowercase!"
>>> filter(lambda c: c not in " ,.;:?!", st.lower())
'onestringwithpuncthatalsoneedslowercase'
So your test can be a one liner that is obvious in function:
>>> str
'!esacrewol sdeen osla tahT .cnup htiw ,gnirts enO'
>>> filter(lambda c: c not in " ,.;:?!", st.lower())==filter(lambda c: c not in " ,.;:?!", str.lower()[::-1])
True
Or, if you are going to use a regex, just reverse the result with the idiomatic str[::-1]:
>>> "123"[::-1]
'321'
>>> re.sub(r'[ ,.;:?!]', '', st.lower())==re.sub(r'[ ,.;:?!]', '', str.lower())[::-1]
True
The fastest may be to use string.tranlate to delete the characters:
>>> import string
>>> string.translate(st, None, " ,.;:?!")
'OnestringwithpuncThatalsoneedslowercase'
>>> string.translate(st, None, " ,.;:?!")==string.translate(str, None, " ,.;:?!")[::-1]
True
When we pass a word it checks if it can be reversed,If it can be reversed it prints "This is a Palindrome". or "This is NOT a Palindrome"
def reverse(word):
x = ''
for i in range(len(word)):
x += word[len(word)-1-i]
return x
word = input('give me a word:\n')
x = reverse(word)
if x == word:
print('This is a Palindrome')
else:
print('This is NOT a Palindrome')
Why not using a more pythonic way!
def palindrome_checker(string):
string = string.lower()
return string == string[::-1] # returns a boolean
Coming from the C/C++ world and being a Python newb, I wrote this simple string function that takes an input string (guaranteed to be ASCII) and returns the last four characters. If there’s less than four characters, I want to fill the leading positions with the letter ‘A'. (this was not an exercise, but a valuable part of another complex function)
There are dozens of methods of doing this, from brute force, to simple, to elegant. My approach below, while functional, didn’t seem "Pythonic".
NOTE: I’m presently using Python 2.6 — and performance is NOT an issue. The input strings are short (2-8 characters), and I call this function only a few thousand times.
def copyFourTrailingChars(src_str):
four_char_array = bytearray("AAAA")
xfrPos = 4
for x in src_str[::-1]:
xfrPos -= 1
four_char_array[xfrPos] = x
if xfrPos == 0:
break
return str(four_char_array)
input_str = "7654321"
print("The output of {0} is {1}".format(input_str, copyFourTrailingChars(input_str)))
input_str = "21"
print("The output of {0} is {1}".format(input_str, copyFourTrailingChars(input_str)))
The output is:
The output of 7654321 is 4321
The output of 21 is AA21
Suggestions from Pythoneers?
I would use simple slicing and then str.rjust() to right justify the result using A as fillchar . Example -
def copy_four(s):
return s[-4:].rjust(4,'A')
Demo -
>>> copy_four('21')
'AA21'
>>> copy_four('1233423')
'3423'
You can simple adding four sentinel 'A' character before the original string, then take the ending four characters:
def copy_four(s):
return ('AAAA'+s)[-4:]
That's simple enough!
How about something with string formatting?
def copy_four(s):
return '{}{}{}{}'.format(*('A'*(4-len(s[-4:])) + s[-4:]))
Result:
>>> copy_four('abcde')
'bcde'
>>> copy_four('abc')
'Aabc'
Here's a nicer, more canonical option:
def copy_four(s):
return '{:A>4}'.format(s[-4:])
Result:
>>> copy_four('abcde')
'bcde'
>>> copy_four('abc')
'Aabc'
You could use slicing to get the last 4 characters, then string repetition (* operator) and concatenation (+ operator) as below:
def trailing_four(s):
s = s[-4:]
s = 'A' * (4 - len(s)) + s
return s
You can try this
def copy_four_trailing_chars(input_string)
list_a = ['A','A','A','A']
str1 = input_string[:-4]
if len(str1) < 4:
str1 = "%s%s" % (''.join(list_a[:4-len(str1)]), str1)
return str1
I have a string like this:
"foo 15 bar -2hello 4 asdf+2"
I'd like to get:
"foo 14 bar -3hello 3 asdf+1"
I would like to replace every number (sequence of digits as signed base-10 integers) with the result of a subtraction executed on each of them, one for each number.
I've written a ~50 LOC function that iterates on characters, separating signs, digits and other text, applying the function and recombining the parts. Although it has one issue my intent with the question is not to review it. Instead I'm trying to ask, what is the pythonic way to solve this, is there an easier way?
For reference, here is my function with the known issue, but my intention is not asking for a review but finding the most pythonic way instead.
edit to answer the wise comment of Janne Karila:
preferred: retain sign if given: +2 should become +1
preferred: zero has no sign: +1 should become 0
preferred: no spaces: asdf - 4 becomes asdf - 3
required: only one sign: -+-2 becomes -+-3
edit on popular demand here is my buggy code :)
DISCLAIMER: Please note I'm not interested in fixing this code. I'm asking if there is a better approach than something like mine.
def apply_to_digits(some_str,handler):
sign = "+"
started = 0
number = []
tmp = []
result = []
for idx,char in enumerate(some_str):
if started:
if not char.isdigit():
if number:
ss = sign + "".join(number)
rewritten = str(handler(int(ss)))
result.append(rewritten)
elif tmp:
result.append("".join(tmp))
number = []
tmp = []
sign = "+"
started = 0
# char will be dealt later
else:
number.append(char)
continue
if char in "-+":
sign = char
started = 1
if tmp:
result.append("".join(tmp))
tmp = []
tmp.append(char)
continue
elif char.isdigit():
started = 1
if tmp:
result.append("".join(tmp))
tmp = []
number.append(char)
else:
tmp.append(char)
if number:
ss = sign + "".join(number)
rewritten = str(handler(int(ss)))
result.append(rewritten)
if tmp:
result.append("".join(tmp)), tmp
return "".join(result)
#
DISCLAIMER: Please note I'm not interested in fixing this code. I'm asking if there is a better approach than something like mine.
You could try using regex, and using re.sub:
>>> pattern = "(-?\d+)|(\+1)"
>>> def sub_one(match):
return str(int(match.group(0)) - 1)
>>> text = "foo 15 bar -2hello 4 asdf+2"
>>> re.sub(pattern, sub_one, text)
'foo 14 bar -3hello 3 asdf+1'
The regex (-?\d+)|(\+1) will either capture an optional - sign and one or more digits, OR the literal sequence +1. That way, the regex will make sure that all of your requirements when converting digits work properly.
The regex (-?\d+) by itself does the right thing most of the time, but the (\+1) exists to make sure that the string +1 always converts to zero, without a sign. If you change your mind, and want +1 to convert to +0, then you can just use only the first part of the regex: (-?d+).
You could probably compress this all into a one-liner if you wanted:
def replace_digits(text):
return re.sub("(-?\d+)|(\+1)", lambda m: str(int(m.group(0)) - 1), text)