I'm taking my first cs course and I'm currently learning the different ways to validate numerical inputs. Below is a bool function I wrote to check comma positions, but when I enter 65,000 it thinks the comma is in the wrong place.
bool commas(string input) {
bool commas = true;
long len = input.length();
int counter = 0;
for(int z = len-1; z >= 0; --z) {
if(counter == 3) {
if(input[z] != ',') {
commas = false;
counter = 0;
}
}
else {
if(input[z] == ',') {
commas = false;
}
else {
++counter;
}
}
}
return commas;
}
The easiest way to figure out if the comma is in the correction position is to go backwards (starting from the rightmost character) within the string.
The reason why this is easier is that if you were to start from the leftmost character and go forward, when you encounter a ,, you don't really know at that point whether that comma is in a valid position. You will only know until later on within the iteration process.
On the other hand, when you go from right-to-left, you know that if you encounter a comma, that comma is in a valid position -- there is no need to wait until you've gone further in the string to determine if the comma is valid.
To do this, it takes an adjustment in the loop to go backwards, and an additional counter to track the current group of 3 digits, since a comma can only occur after a group of 3 digits has been processed.
This is an untested example (except for the simple tests in main):
#include <string>
#include <cctype>
#include <iostream>
bool commas(std::string input)
{
// if the string is empty, return false
if (input.empty())
return false;
// this keeps count of the digit grouping
int counter = 0;
// loop backwards
for (int z = static_cast<int>(input.size()) - 1; z >= 0; --z)
{
// check if the current character is a comma, and if it is in
// position where commas are supposed to be
if (counter == 3)
{
if (input[z] != ',')
return false;
// reset counter to 0 and process next three digits
counter = 0;
}
else
// this must be a digit, else error
if (input[z] == ',')
return false;
else
// go to next digit in group of 3
++counter;
}
// the first character must be a digit.
return isdigit(static_cast<unsigned char>(input[0]));
}
int main()
{
std::string tests[] = { "123,,345",
"123,345",
"123,345,678",
"12,345",
"1223,345",
",123,345",
"1",
"",
"65,00",
"123"};
const int nTests = sizeof(tests) / sizeof(tests[0]);
for (int i = 0; i < nTests; ++i)
std::cout << tests[i] << ": " << (commas(tests[i]) ? "good" : "no good") << "\n";
}
Output:
123,,345: no good
123,345: good
123,345,678: good
12,345: good
1223,345: no good
,123,345: no good
1: good
: no good
65,00: no good
123: good
The way this works is simple -- we just increment a counter and see if the current position we're looking at (position z) in the string is a position where a comma must exist.
The count simply counts each group of 3 digits -- when that group of 3 digits has been processed, then the next character (when going backwards) must be a comma, otherwise the string is invalid. We also check if the current position is where a comma cannot be placed.
Note that at the end, we need to check for invalid input like this:
,123
This is simply done by inspecting the first character in the string, and ensuring it is a digit.
Related
Given a String, find the length of longest substring without any repeating character.
Example 1:
Input: s = ”abcabcbb”
Output: 3
Explanation: The answer is abc with length of 3.
Example 2:
Input: s = ”bbbbb”
Output: 1
Explanation: The answer is b with length of 1 units.
My solution works, but it isn't optimised. How can this be done in O(n) time?
#include<bits/stdc++.h>
using namespace std;
int solve(string str) {
if(str.size()==0)
return 0;
int maxans = INT_MIN;
for (int i = 0; i < str.length(); i++) // outer loop for traversing the string
{
unordered_set < int > set;
for (int j = i; j < str.length(); j++) // nested loop for getting different string starting with str[i]
{
if (set.find(str[j]) != set.end()) // if element if found so mark it as ans and break from the loop
{
maxans = max(maxans, j - i);
break;
}
set.insert(str[j]);
}
}
return maxans;
}
int main() {
string str = "abcsabcds";
cout << "The length of the longest substring without repeating characters is " <<
solve(str);
return 0;
}
Use a two pointer approach along with a hashmap here.
Initialise two pointers i = 0, j = 0 (i and j denote the left and right boundary of the current substring)
If the j-th character is not in the map, we can extend the substring. Add the j-th char to the map and increment j.
If the j-th character is in the map, we can not extend the substring without removing the earlier occurrence of the character. Remove the i-th char from the map and increment i.
Repeat this while j < length of string
This will have a time and space complexity of O(n).
#include <string>
#include <iostream>
#include <vector>
int main() {
// 1
std::string s;
std::cin >> s;
// 2
std::vector<int> lut(127, -1);
int i, beg{ 0 }, len_curr{ 0 }, len_ans{ 0 };
for (i = 0; i != s.size(); ++i) {
if (lut[s[i]] == -1 || lut[s[i]] < beg) {
++len_curr;
}
else {
if (len_curr > len_ans) {
len_ans = len_curr;
}
beg = lut[s[i]] + 1;
len_curr = i - lut[s[i]];
}
lut[s[i]] = i;
}
if (len_curr > len_ans) {
len_ans = len_curr;
}
// 3
std::cout << len_ans << '\n';
return 0;
}
In // 1 you:
Define and read your string s.
In // 2 you:
Define your look up table lut, which is a vector of int and consists of 127 buckets each initialized with -1. As per this article there are 95 printable ASCII characters numbered 32 to 126 hence we allocated 127 buckets. lut[ch] is the position in s where you found the character ch for the last time.
Define i (index variable for s), beg (the position in s where your current substring begin at), len_curr (the length of your current substring), len_ans (the length you are looking for).
Loop through s. If you have never found the character s[i] before OR you have found it but at a position BEFORE beg (It belonged to some previous substring in s) you increment len_curr. Otherwise you have a repeating character ! You compare len_curr against len_ans and If needed you assign. Your new beg will be the position AFTER the one you found your repeating character for the last time at. Your new len_curr will be the difference between your current position in s and the position that you found your repeating character for the last time at.
You assign i to lut[s[i]] which means that you found the character s[i] for the last time at position i.
You repeat the If clause when you fall through the loop because your longest substring can be IN the end of s.
In // 3 you:
Print len_ans.
I am taking a summer course in computer science, and I get assigned two projects a week, so bear with me if I'm getting some terminology wrong.
This week, I was able to get the first one done, but not the second one. In the second assignment, we were required to rewrite the ReadDouble function so that it's more user-friendly; user-friendly as in allowing the user to input commas along with numbers. Additionally,
we were required to allow the first character to be a number, plus or minus sign, or a decimal point.
All other characters can be a number, comma, or decimal point (if there already wasn't one).
As mentioned, commas must be written in properly (we can't allow the user to input 10,00.244, 343,2.334, or some nonsensical number like that).
No commas should be allowed after the decimal point.
Only one decimal point should be allowed in the number.
So far, I was able to able to meet 1), 2), and 5). 3) and 4)? Not so much.
The underlying issue is that I don't know what classes, objects, and whatnot I should be using to make the program read the string input and determine if the commas were inserted properly. I have an idea that I would need to use something similar to the "input.length()" code, set it as a variable that can be compared in an if statement to ensure that the amount of digits until the next comma can be used is met.
I also tried writing a for loop that would check after the decimal place for commas or any other invalid character, but I didn't know what to write down as its initialization. How would I get the for loop to start looking from the decimal after it's aware that one decimal exists?
Another major issue I am encountering is that when I input something like 1.2, it is displayed as 12, meaning that "atof(convert.cstr())" has stripped the decimal from the return value. However, when I enter it as just .2, it comes out as 0.2.
I will provide the code of what I have written so far along with the code of what a friend has suggested to me.
My code:
#include <iostream>
#include <cstdlib>
#include <string>
#include <climits>
using namespace std;
// Main Menu Error Prototype
double ReadDouble(string prompt);
double ReadDouble(string prompt)
{
string input;
string convert;
bool isValid = true;
do {
// Reset the flag to valid input
isValid = true;
// Read input from user
cout << prompt;
cin >> input;
// Validate user input
// Check the first character is a number, + or -
int decimal = 0;
if (input[0] != '+' && input[0] != '-' && input[0] != '.' && isdigit(input[0]) == 0) {
cout << "Error! Input was not an integer.\n";
isValid = false;
}
else {
if (input[0] == '.') {
decimal++;
//cout << "." << endl;
}
convert = input.substr(0, 1);
}
// check that the remaining characters are numeric
long len = input.length();
for (long index = 1; index < len && isValid == true && decimal <= 1; index++) {
if (input[index] == ',') {
; // do nothing if character is a ','
}
else if (input[index] == '.') {
decimal++; // do nothing if character is a '.'
if (decimal > 1) {
cout << "Error! You can have only one decimal point.\n";
isValid = false;
}
}
else if (isdigit(input[index]) == 0) {
cout << "Error! Input was not an integer.\n";
isValid = false;
}
else {
convert += input.substr(index, 1);
}
}
// Start looking where the decimal starts
/*
long decimal=input.find('.');
for (decimal; decimal < len && isValid==true; decimal++) {
if (input[decimal] =='.') {
; // do nothing if character is a '.'
}
}
*/
//cout << "\nDecimal value is " << decimal << endl; -- Test Code
} while (isValid == false);
double returnvalue = atof(convert.c_str());
return returnvalue;
}
int main()
{
double x = ReadDouble("Enter a value: ");
cout << "Value entered was " << x << endl;
return 0;
}
My friend's incomplete code:
ReadDouble(){
isValid = true
do{
get user input and set it to a variable called input
set output variable to a variable called output
bool hasDecimal = false;
int digitsUntilNextComma = 3
for(i = 0; i < input length; i++){
if(input[i] == ','){
if((i < 3 && i > 0) || digitsUntilNextComma == 0){
digitsUntilNextComma = 3;
output += input[i];
}else{ //if it gets to here the comma was in a bad place like ,123 or 12,12,123
isValid = false;
i = input length //breaks out of for loop
}
} else if(input[i] == '.'){
if(i < 3 || digitsUntilNextComma == 0){
if(hasDecimal){ //if this is true then the input had 2 decimals
isValid = false;
i = input length //breaks out of for loop
}else{
hasDecimal = true;
digitsUntilNextComma = 3;
output += input[i];
}
}else{ //if it gets to here, a previous comma was in a bad place like 12,34.56
isValid = false;
i = input length //breaks out of for loop
}
}else{
output += input[i];
digitsUntilNextComma--;
}
}
}while(isValid == false)
}
I hope what I provided wasn't too vague or messy. Again, I had little exposure to programming in the past, so forgive me if I mess some terminology up.
This is a job for a FSM.
Start on + to IN0
Start on - to IN0
Start on Digit to IN1
Start on Decimal to D_prime
IN0 on Decimal to D_prime
IN0 on Digit to IN1
IN1 on Digit to IN2
IN1 on Decimal to D
IN1 on Comma to N0
IN2 on Digit to N3
IN2 on Comma to N0
IN2 on Decimal to D
IN# is "initial numbers, seen #".
D_prime on Digit to D
D on Digit to D
N0 on Digit to N1
N1 on Digit to N2
N2 on Digit to N3
N3 on Comma to N0
N3 on Decimal to D
Valid End states are N3, IN1, IN2, IN3 and D.
Any unexpected character is an input error.
For State, keep an accumulator, a sign, and a decimal count with initial state 0, positive, 0.
On Start(-) set sign to negative.
Whenever you see a digit, multiply accumulator by 10 and add it.
In D or D_prime state, also increase decimal count by 1. D_prime exists to eliminate . as a valid number.
Result is (sign is negative?-1:1)*accumulator/10 to the power of decimal count.
To test this, write a program that generates valid numbers, another that generates gibberish, and another that randomly adds, subtracts, shuffles and splices characters from valid number(s).
The aim of the function is to find out the longest and not repeating substring, so I need to find out the start position of the substring and the length of it. The thing I'm struggling with is the big O notation should be O(n). Therefore I cannot use nested for loops to check whether each letter is repeated.
I created a struct function like this but I don't know how to continue:
struct Answer {
int start;
int length;
};
Answer findsubstring(char *string){
Answer sub={0, 0}
for (int i = 0; i < strlen(string); i++) {
}
return (sub)
}
For example, the input is HelloWorld, and the output should be World.The length is 5.
If the input isabagkfleoKi, then the output is bagkfleoKi. The length is 10.
Also, if the length of two strings is the same, pick the latter one.
Use a std::unordered_map<char, size_t> to store the indices past the last occurance of a certain char.
Keep the currently best match as well as the match you currently test. Iterating through the chars of the input result in 2 cases you need to handle:
the char already occured and the last occurance of the char requires you to move the start of the potential match to avoid the char from occuring twice: Update the answer with the match ending just before the current char, if that's better than the current answer.
Otherwise: Just update the map
void printsubstring(const char* input)
{
std::unordered_map<char, size_t> lastOccurances;
Answer answer{ 0, 0 };
size_t currentPos = 0;
size_t currentStringStart = 0;
char c;
while ((c = input[currentPos]) != 0)
{
auto entry = lastOccurances.insert({ c, currentPos + 1 });
if (!entry.second)
{
if (currentStringStart < entry.first->second && currentPos - currentStringStart > answer.length)
{
// need to move the start of the potential answer
// -> check, if the match up to the char before the current char was better
answer.start = currentStringStart;
answer.length = currentPos - currentStringStart;
currentStringStart = entry.first->second;
}
entry.first->second = currentPos + 1;
}
++currentPos;
}
// check the match ending at the end of the string
if (currentPos - currentStringStart > answer.length)
{
answer.start = currentStringStart;
answer.length = currentPos - currentStringStart;
}
std::cout << answer.start << ", " << answer.length << std::endl;
std::cout << std::string_view(input + answer.start, answer.length) << std::endl;
}
I'll outline one possible solution.
You'll need two loops. One for pointing at the start of the substring and one that points at the end.
auto stringlen = std::strlen(string);
for(size_t beg = 0; beg < stringlen - sub.length; ++beg) {
// See point 2.
for(size_t end = beg; end < stringlen; ++end) {
// See point 3.
}
}
Create a "blacklist" of characters already seen in the substring.
bool blacklist[1 << CHAR_BIT]{}; // zero initialized
Check if the current end character is already in the blacklist and break out of the loop if it is, otherwise, put it in the blacklist.
if(blacklist[ static_cast<unsigned char>(string[end]) ]) break;
else {
blacklist[ static_cast<unsigned char>(string[end]) ] = true;
// See point 4.
}
Check if the length of the current substring (end - beg + 1) is greater than the longest you currently have (sub.length). If it is longer, store sub.start = beg and sub.length = end - beg + 1
Demo and Demo using a bitset<> instead
As the title suggests I want to find all the words in my vector that meet this rule.
My current code is:
void Dictionary::endingIng() {
string findIng;
string foundIng;
int sevenLetters = 7;
for (Word findIng : deffinitions)
if (findIng.getWord().find("ing") <= sevenLetters) {
foundIng = findIng.getWord();
cout << foundIng << endl;
}
However, it also returns words that either contain "ing" somewhere in the middle and words longer then 10 characters, some examples being:
accordingly
barringout
bingo
commandingly
Thanks for the help.
Perhaps this can help.
Adding a case to skip words more than 10 letters.
Also checking the position of "ing" for words <= 10 letters.
Uses rfind for edge case like "inginging"
for (Word findIng : deffinitions) {
std::string word = findIng.getWord();
if (word.length() > 10) {
// skip this - word is more than 10 letters including "ing"
continue;
}
size_t pos = word.rfind("ing");
if (pos == word.length() - 3) {
// we got a match "ing" as suffix of this word
// do something with `word`
}
}
Why don't you (using your current solution) check the length, then get the last 3 values and check if they match.
Or you can go
CString findString = "ing";
int maxLetters = findString.GetLength() + 7; //magic number
foreach(CString s in dictionary)
{
bool b_SmallEnough = (s.Find(findString) <= maxLetters );
bool b_EndsIn = (s.ReverseFind(findString) == (s.GetLength() - findString.GetLength()); //reverse find so it doesn't get the first "ing in bringing and return false because 2 != 5
if(b_SmallEnough && b_EndsIn)
{
std::wcout << s << std::endl;
}
}
Alternatively, bool b_EndsIn could be:
bool b_EndsIn = (s.MakeReverse.Find(findString.MakeReverse()) == 0);
This will mean that your're checking the end first, and always getting the last match
I am trying to perform some operations on an array which the final goal is to do a simple encryption. But anyways my array is 458 characters long which consists of mostly letters and some commas, periods, etc. I am trying to start from last character of array and go to the first character and uppercase all the letters in the array. It reads the last character "" correctly, but then the next step in the for loop is like 4 characters over and skipped a few letters. Is something wrong with my control logic?
void EncryptMessage (ofstream& outFile, char charArray[], int length)
{
int index;
char upperCased;
char current;
for (index = length-1; index <= length; --index)
{
if (charArray[index] >= 'A' && charArray[index] <= 'Z')
{
upperCased = static_cast<char>(charArray[index]);
current = upperCased;
outFile << current;
}
else
{
charArray[index]++;
current = charArray[index];
}
}
}
Change:
for (index = length-1; index <= length; --index)
to:
for (index = length-1; index >= 0; --index)
In the else leg of your if statement, you're setting the value of current, but never writing it out, so all that gets written out are what start as capital letters (and, as others have pointed out, your loop condition isn't correct).
If I were doing this, I'd structure it a bit differently. I'd write a small functor to encrypt a single letter:
struct encrypt {
char operator()(char input) {
if (isupper(input))
return input;
else
return input+1;
}
};
Then I'd put the input into an std::string, and operate on it using std::transform:
std::string msg("content of string goes here.");
std::transform(msg.rbegin(), msg.rend(),
std::ostream_iterator<char>(outFile, ""),
encrypt());