Count number of valid password based on some given restrictions - c++

I have to count how many valid passwords are possible based on some given restrictions. The restrictions are following:
Minimum 10 characters and Maximum 14 characters.
Characters can include small English letters ('a'-'z'), Capital letters ('A'-'Z'), digits ('0'-'9') and special characters ('!','#','#','$','%','^','&','*','(',')')
Valid passwords must contain at least one of small letter, capital letter, digit and special character each.
Passwords cannot contain own student ID. Students ID are of 7 digits. First two digits denote year (00, 01, ... , 99), next two digits denote department code (00, 01, ... 99), and last three digits denote roll inside a department (000 - 180). So a student ID can be like: 1210142, where 12 denotes he is from batch 12, 10 is department code, and 142 is roll number. A student with ID 1210142 cannot have a password like Ti#s1210142mE but can have password like Ti#s121014m2E.
A student can use another student's ID in his/her own password.
Given the restrictions how many valid passwords can be generated?
I wrote a simple C++ program to simulate it. But as it is just a naive implementation, it would require a whole lot of time (maybe greater than my lifetime) to spit out an answer. Is there any clever way to figuring out the answer using code, possibly using regex or something like that?
My effort so far:
#include <iostream>
#include <algorithm>
inline bool is_valid_password(const std::string &generated_password, const std::string &student_id){
bool small = false, captial = false, digit = false, special = false;
for(const char &c : generated_password){
if(c >= 'a' && c <= 'z') small = true;
else if(c >= 'A' && c <= 'Z') captial = true;
else if(c >= '0' && c <= '9') digit = true;
else if(c == '!' || c == '#' || c == '#' || c == '$' || c == '%' || c == '^' || c == '&' || c == '*' || c == '(' || c == ')') special = true;
}
if(small && captial && digit && special){
return generated_password.find(student_id) == std::string::npos;
}
return false;
}
char valid_character_set [] = {
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'0','1','2','3','4','5','6','7','8','9',
'!','#','#','$','%','^','&','*','(',')'
};
long long comb(int N, int K, const std::string &student_id)
{
std::string bitmask(K, 1); // K leading 1's
bitmask.resize(N, 0); // N-K trailing 0's
long long counter = 0;
// print integers and permute bitmask
do {
std::string generated_password = "";
for (int i = 0; i < N; ++i) // [0..N-1] integers
{
if (bitmask[i]){
generated_password += valid_character_set[i];
}
}
if(is_valid_password(generated_password, student_id)) {
//std::cout << "valid password found: " << generated_password << '\n';
counter++;
}
} while (std::prev_permutation(bitmask.begin(), bitmask.end()));
return counter;
}
int main(int argc, char const *argv[])
{
std::cout << "Enter your student id: ";
std::string student_id; std::cin >> student_id;
std::cout << "Your student id is: " << student_id << "\n";
// all possible 10 character passwords
std::cout << comb(72, 10, student_id) << '\n';
return 0;
}

I don't know if you are familiar with writing regex, but if not, you can learn and try out a lot on this website. In my opinion, regex is a very powerful concept and it is worth it to invest some time to learn it.
Anyway, when you are familiar with regex, there is a very useful standard library for regex in C++. Just include
#include<regex>
And then you can instantiated your own regex object:
std::regex r = regex("yourRegexPattern");
With that object, you can invoke some useful functions, like regex_match() (which could be the right one for your use case), regex_search() or regex_replace().
For more information, I have linked the official reference above, or you can just google "c++ regex"
Edit: fixed links

Related

Input validation using character array in C++

I am creating a C++ program to validate book name using a function in c++. The function must return 1 if the input is valid and 0 if the input is invalid. Book name can only contain upper case, lower case characters, color(:), comma(,) and space (there should be no consecutive spaces, commas, and colons). And the maximum characters in a character array is 60.
I tried the following way but I am not getting the desired answer.
const int MAX_BOOK_NAME = 60;
bool isValidBookName(char bookName[MAX_BOOK_NAME])
{
int length = strlen(bookName);
if (length > 59)
{
return false;
}
for (int i = 0; i < 59; i++)
{
if (bookName[i] < 'A' || bookName[i] > 'Z' || bookName[i] < 'a' || bookName[i] > 'z' || bookName[i] != ' ' || bookName[i] != ':' || bookName[i] != ',')
{
return false;
}
}
return true;
}
int main()
{
char arr[60];
cout << "Please Enter Your Book Id : ";
cin.getline(arr, 60);
cout << "Your Entered Name is " << isValidBookName(arr) << endl;
}
bookName[i] < 'A' || bookName[i] > 'Z' || bookName[i] < 'a' || bookName[i] > 'z'
These checks match every character because for example (assuming ASCII or compatible encoding) all capital letters match the condition bookName[i] < 'a' and all smaller case letters match bookName[i] > 'Z'. Since these checks are used to match an invalid character, the check is wrong.
std::isalpha can simplify your program greatly, as pointed out in comments.
The logic of your character check is flawed.
The requirement in your question is that each character must be a letter, or a comma, or a colon, or ... etc. The reverse of that is not "not a letter or not a comma or not a colon or ... etc." The reverse is "not a letter and not a comma and not a colon and ... etc."

Program for getting amount of words that start with Vowels and Consenants is returning wrong answers

Im writing a program for class and so far i have the word counter working fine and the vowel part working fine but Consonants and non alpha digits return wrong answers by 1(in certain cases). i think the problem lies in the testing of the characters them selves but i cant seem to find a way around it.
using namespace std;
int main()
{
char ch;
int count = 0;
int vowel = 0;
int cons = 0;
int noalph = 0;
bool inword = 1;
bool space = 0;
while (cin.get(ch)) {
if (ch != ' ') {
inword = 1;
}
if (isspace(ch)) {
space = 1;
}
else if (space && inword) {
count++;
space = 0;
inword = 0;
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' ||
ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' ||
ch == 'y' || ch == 'Y') {
vowel++;
}
if (ch != 'a' && ch != 'e' && ch != 'i' && ch != 'o' && ch != 'u' &&
ch != 'A' && ch != 'E' && ch != 'I' && ch != 'O' && ch != 'U' &&
ch != 'y' && ch != 'Y'
&& isalpha(ch)) {
cons++;
}
if (ispunct(ch) || isdigit(ch)) {
noalph++;
}
}
}
if (count > 0) {
count++;
}
//--------------------------------------------
cout << "Total Number of Words = " << count << endl;
cout << "Number of Words Beginning with a Vowel = " << vowel << endl;
cout << "Number of Words Beginning with a Consonant = " << cons << endl;
cout << "Number of Words Beginning with a Non-Alpha = " << noalph << endl;
return 0;
}
Example 1(
Input:--------------------------------------------------------------------
supercalifragilisticexpialidocious
A nonsense-word used esp. by children, now chiefly expressing excited
approbation: fantastic, fabulous.
Made popular by the Walt Disney film "Mary Poppins" in 1964. The
song containing the word was the subject of a copyright infringement
suit brought in 1965 against the makers of the film by Life Music
Co. and two song-writers: cf. quots. 1949, 1951. In view of earlier
oral uses of the word sworn to in affidavits and dissimilarity between
the songs the judge ruled against the plaintiffs.
Taken from the OED.
Output:--------------------------------------------------------------------
Total Number of Words = 86
Number of Words Beginning with a Vowel = 25
Number of Words Beginning with a Consonant = 55
Number of Words Beginning with a Non-Alpha = 5
Expected:------------------------------------------------------------------
Total Number of Words = 86
Number of Words Beginning with a Vowel = 25
Number of Words Beginning with a Consonant = 56
Number of Words Beginning with a Non-Alpha = 5
)
Example 2(
Input:--------------------------------------------------------------------
1996
bottle
12345
radar
a Toyota
Madam, I'm Adam
Was it a rat I saw?
Norma is as selfless as I am, Ron.
A man, a plan, a canal--Panama!
Tarzan raised Desi Arnaz' rat.
Hannah
Lewd did I live, & evil I did dwel.
Excerpts from "The Zen of Programming"
Mary said, "I like the STL."
Output:--------------------------------------------------------------------
Total Number of Words = 56
Number of Words Beginning with a Vowel = 20
Number of Words Beginning with a Consonant = 31
Number of Words Beginning with a Non-Alpha = 4
Expected:------------------------------------------------------------------
Total Number of Words = 56
Number of Words Beginning with a Vowel = 20
Number of Words Beginning with a Consonant = 31
Number of Words Beginning with a Non-Alpha = 5
)
As you can see it breaks at different points, maybe its something simple maybe it not, i would just like some help understanding what is going on, Thank You!
OK, you didn't followed advices in comments, so let do it explicitly.
You are skipping the first word.
Cause of the initialization in this line:
bool space = 0;
you should initialize it as true
bool space = true;
(yes, use true and false for boolean).
you should see that cause you added this lines
if (count > 0) {
count++;
}
to cover this problem. So, remove them.
You also skipped the #include <iostream> directive.

C++ how to cout once instead of multiple times in a for loop

void f3(string x){
for (int i=0; i < x.length(); i++){
if ('A'<=x[i] && 'z'>=x[i]){
cout << "English Alphabet" << endl;
}
else {
cout << "NOT English Alphabet" << endl;
break;
}
}
}
how to get only one result (cout)? like "English Alphabet" instead of 4 times for "abcd".
examples:
gimme a string:
abcd
English Alphabet
English Alphabet
English
Alphabet
English Alphabet
or
gimme a string:
abc!
English Alphabet
English Alphabet
English
Alphabet
NOT English Alphabet
and if I input space the program does not work.
How could I fix it?
Thanks for the help!
I am not sure if I got what you want, and I do not know C++, but something that may help you is using a boolean to define whether or not the input has non-English characters in it. You would define it as true from the beginning, and as soon as you find a character that isn't an English character, you set it to false. When you step out of the for loop, you use an if to check if your input has only English characters. It would be something like this:
void f3(string x) {
bool isEnglish = true;
for (int i=0; i < x.length(); i++){
if ('A'>x[i] || 'z'<x[i]){
isEnglish = false;
break;
}
}
if (isEnglish) {
cout << "English Alphabet" << endl;
} else {
cout << "NOT English Alphabet" << endl;
}
}
If you input space the program should run OK, but it will always return "not english alphabet" for it. That's because the test you're making takes into consideration if the current character of the input is between characters A and z of the ASCII character space. See the image below:
See how space is character number 32, which is smaller than 'A', and therefore, will not enter your if block, and fall inside the else, thus returning 'NOT English alphabet'. Numbers, the # sign, the & sign and everything before 'A' and everything after 'z' will also fail the test. Also, brackets ('[' and ']') will pass as English alphabet input.
If you want to only include letters, your if should look like:
if (x[i] < 'A' || (x[i] > 'Z' && x[i] < 'a') || x[i] > 'z')
If you want to include spaces, add another &&:
if ((x[i] < 'A' || (x[i] > 'Z' && x[i] < 'a') || x[i] > 'z') && x[i] != ' ')
The same logic can be used to add any characters to your verification.
Good luck!
Since you've tagged this as C++, I'm going to assume you're prefer a solution written in real C++.
First, I'd look up std::isalpha. It's defined specifically to determine whether a character is alphabetic1.
Second, I'd look up std::all_of and std::any_of. They're defined to determine whether any/all of a collection fits a particular requirement.
Putting those together, you could do something like this:
if (!std::all_of(x.begin(), x.end(),
[](unsigned char c) { return std::isalpha(c); })
std::cout << "Not ";
std::cout << "English alphabet\n";
1. Note that the result of isalpha isn't necessarily based on the English alphabet. Rather, it's based on the the current locale, so with (for example) a French locale, it would say that not only was an "a" alphabetic, but also that "â" and "à' were alphabetic as well (which your test would not). Likewise, for a German locale it would say "ß" was alphabetic (and so on). The default locale is named "C"; for things like determining whether something is alphabetic, it follows the English alphabet.
Here two solutions:
With a bool variable:
void f3(string x)
{
bool notEnglish = false;
for (int i=0; i < x.length(); i++)
{
if ( ('A' > x[i] || 'z' < x[i]) && x[i] != ' ' )
{
notEnglish = true;
break;
}
}
if (notEnglish) std::cout << "NOT ENGLISH" << std::endl;
else std::cout << "ENGLISH" << std::endl;
}
Without a bool and with int as return value:
int f3(string x)
{
for (int i=0; i < x.length(); i++)
{
if ( ('A' > x[i] || 'z' < x[i]) && x[i] != ' ' )
{
std::cout << "NOT ENGLISH" << std::endl;
return -1;
}
}
std::cout << "ENGLISH" << std::endl;
return 1;
}
I prefer the second one since you have a feedback (the return value) of what is occured inside the function.
You can declare a local Boolean variable so as soon as non-English detected the loop breaks immediately setting the the variable to false.
The matter is trickier: Checking for non-English characters:
void f3(string x){
bool IsEng = true;
for(int i = 0; i < x.length(); i++){
if ( !( x[i] >= 'A' && x[i] <= 'z') ){
IsEng = false;
break;
}
}
if(IsEng)
cout << "English Alphabet" << endl;
else
cout << "Not English Alphabet" << endl;
}
This is very simple and logical way to execute a line of code once:
int x=1
if (x==1)
{
//your cout-code here;
x++;
}
x becomes 2, so next time it won't be executed

C - Breaking up a string using find() [duplicate]

This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 9 years ago.
I am writing a pig latin converter that takes a string as a command line argument and converts each word in the sentence into pig latin. I am trying to break up the string into individual words by finding spaces, but the output I get with the below code... honestly makes no sense.
It works for the first word, but then doesn't convert any subsequent words. So "test this string" becomes "esttay this ay string ay."
At this point we haven't covered vectors, so the solution has to be relatively simple.
Any ideas?
#include <iostream>
#include <string>
using namespace std;
void convertSentence(string sentence);
string pigLatinWord(string input);
bool isVowel(string input);
int main(int argc, char* argv[]) {
if(argc != 2) {
cout << "USAGE: " << argv[0] << " \"[sentence]\"" << endl;
} else {
string sentence(argv[1]);
convertSentence(sentence);
}
return 0;
}
void convertSentence(string sentence) {
string word = "", remainder = sentence, pigLatin = "";
for(int i = sentence.find(" ",0); i != string::npos; i = sentence.find(" ",i)) {
word = sentence.substr(0,i);
pigLatin += pigLatinWord(word) + " ";
sentence = sentence.erase(0,i);
i++;
}
pigLatin += pigLatinWord(sentence);
cout << pigLatin << endl;
}
string pigLatinWord(string input) {
string word = "";
// If the first letter is a vowel, simply add "yay" to the end of it.
if (isVowel(input)) {
word = input + "yay";
//If the first letter is a consonant, add the first letter to the end,
//delete the first letter, then add "ay." - Credit to Tyler Sorrels
//CString/String solution post on Piazza. I had issues working with
//substrings, and this ended up being easier to read.
//But I did add a check for words that start with two consonants
//to cover all cases.
} else {
input += input.at(0);
input = input.erase(0,1);
word = input + "ay";
}
return word;
}
// Checks if the first letter of the word is a vowel.
// Returns true if yes, false if no.
bool isVowel(string input) {
return ((input[0] == 'a') || (input[0] == 'e') || (input[0] == 'i') || (input[0] == 'o') || (input[0] == 'a'));
}
Two errors:
for(int ...; ; i = sentence.find(" ",i)) { // next space find should start from 0.
//..
//..
sentence = sentence.erase(0,i);// you should delete the current space found also.
i++;
}
Change this to:
for(int ...; ; i = sentence.find(" ",0)) {
//..
//..
sentence = sentence.erase(0,i+1);
i++;
}
Output:
esttay histay tringsay
The code is confused because you are trying to skip the word you have already found and you are trying to remove the word you have already found from sentence. Do one or the other.
Can I recommend that you use the POSIX regular expression library or PCRE, which should make swapping the first and last letters of words simple.
A regex such as \b(\w)(\w+)(\w)\b could be replaced by swapping the first and last collection groups

Integer input restricted to four digits only

I'm doing a problem where it asks to input an account number, which consists only of four digits. This has to be accomplished with basic beginner C++.
I need to figure out a way to restrict the input of the integer to four digits. A user should be able to put in 0043 or 9023 or 0001 and it should be an acceptable value....
I think I know how to accomplish it with a string.... getline(cin,input) and then check if input.length()==4?
But I've no idea how I would even do this with an integer input.
Note that if 0043 is intended to be distinct from 43, then the input is not in fact a number, but a digit string, just like a telephone "number".
Read the line as a string input.
Check that the length of input is 4.
Check that each character in the string is <= '9' and >= '0'.
Something like:
std::string read4DigitStringFromConsole()
{
bool ok = false;
std::string result;
while (!ok)
{
std::cin >> result;
if (result.length() == 4)
{
bool allDigits = true;
for(unsigned index = 0; index < 4; ++index)
{
allDigits = allDigits && (
(result[index] >= '0') &&
(result[index] <='9')
);
}
ok = allDigits;
}
}
return result;
}
Something like this should work. Once the user enters something with exactly four characters you can validate it. The rest of the logic is up to you.
#include <iostream>
#include <string>
int main() {
std::cout << "Enter a PIN Number: ";
std::string pinStr;
while(std::getline(std::cin,pinStr) && pinStr.size() != 4) {
std::cout << "Please enter a valid value\n";
}
}
Should you want to store it in an integer form, holding the integers in an std::vector might be beneficial. You can do this easily (loop unrolling was for clarity):
#include <iostream>
#include <string>
#include <vector>
int main() {
std::cout << "Enter a PIN Number: ";
std::string pinStr;
while(std::getline(std::cin,pinStr) && pinStr.size() != 4 ) {
std::cout << "Please enter a valid value\n";
}
std::vector<int> pin;
pin[0] = pinStr[0] - '0';
pin[1] = pinStr[1] - '0';
pin[2] = pinStr[2] - '0';
pin[3] = pinStr[3] - '0';
//pin now holds the integer value.
for(auto& i : pin)
std::cout << i << ' ';
}
You can see it running here
I like your idea to use a string as the input. This makes sense because an account "number" is simply an identifier. You don't use it in calculations. By if (sizeof(input)==4) I think you are trying to check the length of the string. The correct way to do this is if (input.length() == 4). This will check that the user inputs 4 characters. Now you need to make sure that each of the characters is also a digit. You can do this easily by taking advantage of the fact that the ASCII codes for digit characters are ordered as you expect. So if (input[i] >= '0' && input[i] <= '9') will do the trick with an appropriate for loop for the index i. Also, you probably need some kind of loop which continues to ask for input until the user enters something which is deemed to be correct.
Edit:
As an alternative to checking that each character is a digit, you can attempt to convert the string to an int with int value = atoi(input.c_str());. Then you can easily check if the int is a four-or-less-digit number.
// generic solution
int numDigits(int number)
{
int digits = 0;
if (number < 0) digits = 1; // remove this line if '-' counts as a digit
while (number) {
number /= 10;
digits++;
}
return digits;
}
similar to this post.
Then you can call this function to check if the input is 4 digits.
You probably want your code to be responsive to the user input, so I would suggest getting each character at a time instead of reading a string:
std::string fourDigits;
char currentDigit;
std::cout << "Enter 4 digits\n";
for(int i = 0; i < 4; ++i)
{
currentDigit = getch();
if(isdigit(currentDigit))
{
fourDigits += currentDigit;
std::cout << currentDigit; // getch won't display the input, if it was a PIN you could simply std::cout << "*";
}
else
{
// Here we reset the whole thing and let the user know he entered an invalid value
i = 0;
fourDigits = "";
std::cout << "Please enter only numeric values, enter 4 digits\n";
}
}
std::cout << "\nThe four digits: " << fourDigits.c_str();
This way you can handle gracefully invalid character instantly. When using strings, the input will only be validated once the user hits Enter.
So I was going over how I can use an integer type to get the input, and looked at char... since it's technically the smallest integer type, it can be used to get the code... I was able to come up with this, but it's definitely not refined yet (and I'm not sure if it can be):
int main() {
int count=0;
while(!(count==4)){
char digit;
cin.get(digit);
count++;
}
return 0;
}
So, the loop keeps going until 4 characters are collected. Well, in theory it should. But it doesn't work. It'll stop at 2 digits, 5 digits, etc.... I think it could be the nature of cin.get() grabbing white space, not sure.