I am making a simple program to check if a UID is valid or not:
It must contain at 2 least uppercase English alphabet characters.
It must contain at 3 least digits (0-9).
It should only contain alphanumeric characters (a-z, A-Z & 0-9).
No character should repeat.
There must be exactly 10 characters in a valid UID.
This is the code I'm using:
if __name__ == '__main__':
n = int(input())
values = [str(input()) for q in range(1,n+1)]
def checker():
cap_count = 0
num_count = 0
alnm_count = 0
for x in values[:]:
for p in x:
if p.isupper():
cap_count = cap_count+1
if cap_count > 1 and p.isnumeric():
num_count = num_count+1
if (len(x) == 10) and (x.isalnum()) and (len(x) == len(set(x))) and (cap_count > 1) and (num_count > 2):
print("Valid")
else:
print("Invalid")
checker()
Sample Input:
2
B1CD102354
B1CDEF2354
So, on giving these inputs, it works well. I have tried it up to 6-7 inputs and it works.
There is no error, it's just not giving the answer on one test case with 90 values.
Here is a link to the test that I am attempting
There's no reason that the numeric test is dependent on the uppercase test.
Change:
if cap_count > 1 and p.isnumeric():
to:
if p.isnumeric():
Also, the counters should be reset for every new value, so move the counter initialization inside the loop:
for x in values[:]:
cap_count = 0
num_count = 0
...
This seems to be the Hackerrank problem
Following code for conditions works.
def checker(uid):
if len(uid) != 10: return False
if not uid.isalnum(): return False
if len(uid) != len(set(uid)): return False
if len([c for c in uid if c.isdigit()]) < 3: return False
if len([c for c in uid if c.isupper()]) < 2: return False
return True
if __name__ == '__main__':
n = int(input())
for _ in range(n):
if checker(input()):
print("Valid")
else:
print("Invalid")
This is the offending line:
if cap_count > 1 and p.isnumeric():
which is actually adding an unwanted constraint, i.e. making digits acceptable only if at least 2 capitals were found.
By the way, this is the sort of data validation that regex is designed for, meaning that you can do all those checks in a couple of lines:
import re
pattern = r"^(?=(?:[a-z\d]*[A-Z]){2})(?=(?:\D*\d){3})(?:([a-zA-Z\d])(?!.*\1)){10}$"
regex = re.compile(pattern)
if regex.match("uid_here"):
do_something
I strongly suggest you delve into re documentation (especially the howto section) if you're willing to dissect that weird pattern and know exactly what it does.
But for the sake of summarizing a TL;DR, each element of the pattern defines a specific rule that the string must comply with to match the pattern, i.e:
^ and $ represent the ends of the string
(?=(?:[a-z\d]*[A-Z]){2}) this makes sure that at least 2 capitals are found
(?=(?:\D*\d){3}) this makes sure that at least 3 digits are found
(?:([a-zA-Z\d])(?!.*\1)){10} this makes sure that the string has exactly 10 characters with no repetition allowed
The second condition seems to accept digits only if at least two upper case letters were found before. In other words, digits appearing before the second upper case letter are not counted. It's due to the cap_count > 1 term in the condition.
In the example "123ABCDEFG" from my comment above, all digits appear before all letters, so the digit counter remains zero and the final test (num_count > 2) fails.
Related
“All vanity plates must start with at least two letters.”
“… vanity plates may contain a maximum of 6 characters (letters or numbers) and a minimum of 2 characters.”
“Numbers cannot be used in the middle of a plate; they must come at the end. For example, AAA222 would be an acceptable … vanity plate; AAA22A would not be acceptable. The first number used cannot be a ‘0’.”
“No periods, spaces, or punctuation marks are allowed.”
I solved ever problem but i am having a difficult time solving the highlighted problem. AAA22A or AAA2A as invalid.
def is_valid(s):
# checks for at least 2 chars and a max of 6 characters
# and checks for at least 2 uppercase letters
if len(s) < 2 or len(s) > 6 and s[0:2].isalpha():
return False
# checks for periods, spaces, or punctuation marks
for i in s:
if i in [".", " ", "!", "?"]:
return False
# checks if numbers in string starts with 0
i = 0
while i < len(s):
if s[i].isalpha() == False:
if s[i] == "0":
return False
else:
break
i += 1
# after u see a valid number, no letter can come after it
# this is where the challenge is ,
for i in s[::-1]:
if s[i].isalpha() == True:
for j in s[::-2]:
if s[j].isalpha == False:
return False
else:
break
else:
break
# returns true if all the requirements are passed
return True
"""i tried to iterate in reverse order but that deems unsuccessful. Not sure how to solve. need help please."""
You can have a function to know if a letter comes after a number or use this piece of code directly:
def letter_after_number(s):
for i in range(len(s) - 1):
if s[i].isdigit() and s[i + 1].isalpha():
return False
return True
here the entire code:
def is_valid(s):
# checks for at least 2 chars and a max of 6 characters
# and checks for at least 2 uppercase letters
if len(s) < 2 or len(s) > 6 and s[0:2].isalpha():
return False
# checks for periods, spaces, or punctuation marks
for i in s:
if i in [".", " ", "!", "?"]:
return False
# checks if numbers in string starts with 0
i = 0
while i < len(s):
if s[i].isalpha() == False:
if s[i] == "0":
return False
else:
break
i += 1
# after u see a valid number, no letter can come after it
# this is where the challenge is ,
for i in range(len(s) - 1):
if s[i].isdigit() and s[i + 1].isalpha():
return False
# returns true if all the requirements are passed
return True
So, you don't have to go in reverse order to check if there's a letter. I would iterate over the letters until you get to a character that's a number. Once you do that, you can check that the rest of the characters are numbers.
If you did want to go in reverse, you can apply the same logic: iterate backwards, checking that you're only reading numbers. Once you hit a letter, check that the rest of the string is only letters. You can do this in multiple ways, such as using set operations or just checking the character is the correct type.
Also, note that you don't have to/shouldn't use True/False in the conditional statements. It's better to use something like if s[i].isalpha() and if not s[i].isalpha()
This could be a good use of regular expressions. This pattern only matches strings that have an optional group of letters followed by an optional group of numbers. The pattern also omits special characters by design.
import re
def is_valid(s):
if len(s) < 2 or len(s) > 6:
return False
# regular expression with two capture groups
pattern = "^([a-zA-Z]{0,6})([0-9]{0,6})$"
m = re.match(pattern, s)
if m is None:
return False
alpha = m.group(1)
numeric = m.group(2)
# first digit is not zero
if numeric[0]=="0":
return False
return True
I am asking the user to input a password. The program then determines if the password is valid if it meets certain criteria.
Here is the section in question.
for i in range(0, len(password)):
if(password[i].isdigit()==True):
isNum+=1
elif (password[i].isalpha()==True):
isLetter+=1
elif (password[i].isupper()==True):
isUpper+=1
My program detects numbers and letters just fine, but it doesn't detect uppercase letters. If I put in the password 1234Foxes, it will say that there are 4 letters, 4 numbers, and 8 total characters, but it states that there are 0 uppercase letters.
If isupper is true, isalpha must have been true since an uppercase letter is necessarily alphabetic. Since you're using elif, the conditions are exclusive, and the checks stop once the isalpha check is true.
Just don't use elif there if you want both checks to run:
for character in password:
if(character.isdigit()):
isNum += 1
elif (character.isalpha()):
isLetter += 1
if (character.isupper()):
isUpper += 1
I also got rid of the need to index password by iterating the String directly, and the redundant == True
your logic is correct, but, you need to check if a char is upper before if it is alpha. It's because every upper char is alpha, so, the last elif will never be reached. So, change your code position to this:
for i in range(0, len(password)):
if(password[i].isdigit()==True):
isNum+=1
# isupper() first from isalpha()
elif (password[i].isupper()==True):
isUpper+=1
elif (password[i].isalpha()==True):
isLetter+=1
And, another tip: you can go through every char of string without using indexing, like this:
for char in password:
if (char.isdigit()):
isNum += 1
elif (char.isupper()):
isUpper += 1
elif (char.isalpha()):
isLetter += 1
Since isdigit(), isalpha() and isupper() returns True or False, you don't need to check if it is True or False (no need the == operator), just put it into if statement.
testcases = int(input())
for i in range(testcases):
n = int(input())
names = []
for a in range(n):
names.append(input())
prefix = ''
for b in range(len(names[0])):
for c in names:
if c.startswith(prefix) == True:
common = True
else: common = False
if common == False:
break
prefix += names[0][b]
print(prefix)
I am given a list of names and I need to find the common prefix that applies to every name. My program works, but always returns one more letter than is supposed to be there. Why is this, and how do I fix it?
If the current prefix matches all the entered names, you add one more character to it. When it fails to match, you break out of the loop - but the character that caused the failure is still attached to the end of prefix.
There are various ways to fix this, but one possibility is to just remove the last character by adding this statement outside the loop:
prefix = prefix[:-1] # python slice notation - remove the last element
There are some style issues with your code. Those would be best addressed on CodeReview rather than Stackoverflow.
I would have done it this way (after replacing your input statements with a hard-coded test case):
x = ["Joseph", "Jose", "Josie", "Joselyn"]
n = 0
try:
while all(a[n] == x[0][n] for a in x[1:]):
n += 1
except IndexError:
pass
print(x[0][:n])
This script prints "Jos".
My code intends to identify the first non-repeating string characters, empty strings, repeating strings (i.e. abba or aa), but it's also meant to treat lower and upper case input as the same character while returning the accurate non-repeating character in it's orignial case input.
def first_non_repeat(string):
order = []
counts = {}
for x in string:
if x in counts and x.islower() == True:
counts[x] += 1
else:
counts[x] = 1
order.append(x)
for x in order:
if counts[x] == 1:
return x
return ''
My logic on line 5 was that if I make all letter inputs lowercase, then it would iterate through the string input and not distinguish by case. But as of now, take the input 'sTreSS'and output is 's' when really I need 'T'. If the last two S's were lowercase, then it would be 'T' but I need code flexible enough to handle any case input.
When comparing two letters, use lower() to compare the characters in a string. An example would be:
string ="aabcC"
count = 0
while count < len(string) - 1:
if string[count].lower() == string[count + 1].lower():
print "Characters " + string[count] + " and " + string[count + 1] + " are repeating."
count += 1
Here's little change u can make to your code to make it work.
def first_non_repeat(string):
order = []
counts = {}
for x in string:
char_to_look = x.lower() #### convert to lowercase for all operations
if char_to_look in counts :
counts[char_to_look] += 1
else:
counts[char_to_look] = 1
order.append(char_to_look)
for x in string: ### search in the string instead or order, character and order will remain the same, except the case. So again do x.lower() to search in count
if counts[x.lower()] == 1:
return x
return ''1
The point is that x in counts is searched for in a case-insensitive way. You have to implement your own case insensitive Dictionary, or use regular expressions to detect repeating letters:
import re
def first_non_repeat(string):
r = re.compile(r'([a-z])(?=.*\1)', re.I|re.S)
m = r.search(string)
while m:
string = re.sub(m.group(1), '', string, re.I)
m = r.search(string)
return string[0]
print(first_non_repeat('sTreSS'))
See the Python demo
The ([a-z])(?=.*\1) regex finds any ASCII letter that also appears somewhere ahead (note that ([a-z]) captures the char into Group 1 and the (?=.*\1) is a lookahead where \1 matches the same char captured into Group 1 after any 0+ characters matched with .* pattern, and re.S flag helps support strings with linebreaks).
The re.sub will remove all the found letters in a case insensitive way, so we will only get unique characters in the string after the while block.
I am trying to count the number of times 'e' appears in a word.
def has_no_e(word): #counts 'e's in a word
letters = len(word)
count = 0
while letters >= 0:
if word[letters-1] == 'e':
count = count + 1
letters = letters - 1
print count
It seems to work fine except when the word ends with an 'e'. It will count that 'e' twice. I have no idea why. Any help?
I know my code may be sloppy, I'm a beginner! I'm just trying to figure out the logic behind what's happening.
>>> word = 'eeeooooohoooooeee'
>>> word.count('e')
6
Why not this?
As others mention, you can implement the test with a simple word.count('e'). Unless you're doing this as a simple exercise, this is far better than trying to reinvent the wheel.
The problem with your code is that it counts the last character twice because you are testing index -1 at the end, which in Python returns the last character in the string. Fix it by changing while letters >= 0 to while letters > 0.
There are other ways you can tidy up your code (assuming this is an exercise in learning):
Python provides a nice way of iterating over a string using a for loop. This is far more concise and easier to read than using a while loop and maintaining your own counter variable. As you've already seen here, adding complexity results in bugs. Keep it simple.
Most languages provide a += operator, which for integers adds the amount to a variable. It's more concise than count = count + 1.
Use a parameter to define which character you're counting to make it more flexible. Define a default argument for using char='e' in the parameter list when you have an obvious default.
Choose a more appropriate name for the function. The name has_no_e() makes the reader think the code checks to see if the code has no e, but what it actually does is counts the occurrences of e.
Putting this all together we get:
def count_letter(word, char='e'):
count = 0
for c in word:
if c == char:
count += 1
return count
Some tests:
>>> count_letter('tee')
2
>>> count_letter('tee', 't')
1
>>> count_letter('tee', 'f')
0
>>> count_letter('wh' + 'e'*100)
100
Why not simply
def has_no_e(word):
return sum(1 for letter in word if letter=="e")
The problem is that the last value of 'letters' in your iteration is '0', and when this happens you look at:
word[letters-1]
meaning, you look at word[-1], which in python means "last letter of the word".
so you're actually counting correctly, and adding a "bonus" one if the last letter is 'e'.
It will count it twice when ending with an e because you decrement letters one time too many (because you loop while letters >= 0 and you should be looping while letters > 0). When letters reaches zero you check word[letters-1] == word[-1] which corresponds to the last character in the word.
Many of these suggested solutions will work fine.
Know that, in Python, list[-1] will return the last element of the list.
So, in your original code, when you were referencing word[letters-1] in a while loop constrained by letters >= 0, you would count the 'e' on the end of the word twice (once when letters was the length-1 and a second time when letters was 0).
For example, if my word was "Pete" your code trace would look like this (if you printed out word[letter] each loop.
e (for word[3])
t (for word[2])
e (for word[1])
P (for word[0])
e (for word[-1])
Hope this helps to clear things up and to reveal an interesting little quirk about Python.
#marcog makes some excellent points;
in the meantime, you can do simple debugging by inserting print statements -
def has_no_e(word):
letters = len(word)
count = 0
while letters >= 0:
ch = word[letters-1] # what is it looking at?
if ch == 'e':
count = count + 1
print('{0} <-'.format(ch))
else:
print('{0}'.format(ch))
letters = letters - 1
print count
then
has_no_e('tease')
returns
e <-
s
a
e <-
t
e <-
3
from which you can see that
you are going through the string in reverse order
it is correctly recognizing e's
you are 'wrapping around' to the end of the string - hence the extra e if your string ends in one
If what you really want is 'has_no_e' then the following may be more appropriate than counting 'e's and then later checking for zero,
def has_no_e(word):
return 'e' not in word
>>> has_no_e('Adrian')
True
>>> has_no_e('test')
False
>>> has_no_e('NYSE')
True
If you want to check there are no 'E's either,
def has_no_e(word):
return 'e' not in word.lower()
>>> has_no_e('NYSE')
False
You don't have to use a while-loop. Strings can be used for-loops in Python.
def has_no_e(word):
count = 0
for letter in word:
if letter == "e":
count += 1
print count
or something simpler:
def has_no_e(word):
return sum(1 for letter in word if letter=="e")