Add items to a vector recursively - c++

I'm attempting to create a recursive function that outputs a vector of strings that contains all possible word combinations (while retaining order of letters) of a given string. Basically, the foundation of an auto-correct typing program, which produces effects similar that of the iPhone.
vector<string> allPossibleWords(string str, vector<vector<char> > & adjacentKeys)
{
vector<string> words;
cout << str << endl;
if (str.length() == 0)
{
return words;
}
char firstLetter = str[0];
string restOf = str.substr(1, str.length() - 1);
int position = position_in_vector(firstLetter);
for (int i = 0; i < adjacentKeys[position].size(); i++)
{
string temp(1, adjacentKeys[position][i]);
words.push_back(temp);
}
//allPossibleWords(restOf, adjacentKeys);
}
int position_in_vector(char letter)
{
return (letter % 97);
}
For instance, if str is "yp", the output should be a vector containing the values {"yp", "tp", "gp", "hp", "up", "yo", "to", "go", "ho", "uo", "yl", "tl", "gl", "hl", "ul"}. If str is "y", the output should be a vector containing the values {"y", "t", "g", "h", "u"}.
The 26 vectors stored in adjacentKeys contain the letters adjacent to the letter that is stored in the first position of the vector.
a qwsz
b vghjn
c xdfgv
d zserfcx
//and so on
I am stuck with this function, and can't figure out how to recursively build this vector.

(Update: 2130 GMT Sunday: I've significantly changed my answer. I think this works now.)
Here is a complete program. There are other changes I think I would make, but I'm trying to keep to the spirit of your initial solution. It's important to return a single empty word when str.length()==0.
#include <vector>
#include <iostream>
using namespace std;
vector<string> allPossibleWords(string str, vector<vector<char> > & adjacentKeys)
{
vector<string> words;
// cout << "str=" << str << endl;
if (str.length() == 0)
{
words.push_back("");
return words;
}
char firstLetter = str[0];
// cout << "firstLetter=" << firstLetter << endl;
int positionInAdjacentKeys = 0;
while(positionInAdjacentKeys < adjacentKeys.size() && adjacentKeys.at(positionInAdjacentKeys).front() != firstLetter) {
++ positionInAdjacentKeys;
}
vector<char> & adjacent = adjacentKeys.at(positionInAdjacentKeys);
string restOf = str.substr(1, str.length() - 1);
// cout << firstLetter << ":" << restOf << endl;
// int position = position_in_vector(firstLetter);
vector<string> recursiveWords = allPossibleWords(restOf, adjacentKeys);
for (int i = 0; i < adjacent.size(); i++)
{
const string temp(1, adjacent[i]);
// cout << " temp=" << temp << endl;
for(vector<string>::const_iterator i = recursiveWords.begin(); i != recursiveWords.end(); i++)
{
// cout << "new word=" << temp + *i << endl;
words.push_back(temp + *i);
}
}
return words;
}
int main() {
vector<vector<char> > adj;
vector<char> v1;
v1.clear();
v1.push_back('p');
v1.push_back('o');
v1.push_back('l');
adj.push_back(v1);
v1.clear();
v1.push_back('y');
v1.push_back('t');
v1.push_back('g');
v1.push_back('h');
v1.push_back('u');
adj.push_back(v1);
adj.push_back(v1);
vector<string> words = allPossibleWords("yp", adj);
for(vector<string> :: const_iterator i = words.begin(); i != words.end(); i++) {
cout << *i << endl;
}
}
return

Maybe something like this? I haven't tested it because I don't have your adjacentKeys matrix. It can probably be optimised a bit, but I don't think this approach will scale well at all.
I'd suggest attacking the problem from a different angle, perhaps storing your dictionary in some kind of K-ary tree, and having several pointers walking the tree, following branches based on your adjacency matrix. This would stop the generation of invalid words (and subsequent lookups to check validity) as branches would only exist where valid words exist.
using namespace std;
void allPossibleWordsHelper(const string& str,
string::size_type index,
const vector<vector<char> >& adjacentKeys,
vector<string>& results)
{
if (str.length() == 0)
{
return;
}
std::string head = (index > 0) ? str.substr(0, index) : "";
std::string tail = (index < str.length() - 1) ? str.substr(index + 1) : "";
vector<string> possibleHeads;
string::size_type headIndex = (str.length() - index) / 2;
allPossibleWordsHelper(head, headIndex, adjacentKeys, possibleHeads);
vector<string> possibleTails;
allPossibleWordsHelper(tail, index + headIndex, adjacentKeys, possibleTails);
int pos = str[index] - 'a';
vector<string>::const_iterator headi;
vector<string>::const_iterator headi_end = possibleHeads.end();
vector<string>::const_iterator taili;
vector<string>::const_iterator taili_end = possibleTails.end();
vector<char>::const_iterator aki;
vector<char>::const_iterator aki_end = adjacentKeys[pos].end();
for(headi = possibleHeads.begin(); headi != headi_end; ++headi)
{
for (aki = adjacentKeys[pos].begin(); aki != aki_end; ++aki)
{
for (taili = possibleTails.begin(); taili != taili_end; ++taili)
{
string suggestedWord = *headi + *aki + *taili;
results.push_back(suggestedWord);
}
}
}
}

Related

Remove the words from a string that start with a certain character

I have to create a function in C++ that would remove all the words from a string that start with a certain character inputted by a user. For example, if I were to have a string "She made up her mind to meet up with him in the morning" and a substring "m", I would like my string to be "She up her to up with him in the".
I believe I would need to find the occurrences of "m", erase it and all the characters after it till the space " ". Would that be the right approach and if so what would be the best methods to use in this case?
With your kind help I have altered and added code a little bit. The first function 'GetNextWord' seems to be working alright, however, there is definitely something wrong with my function, which is supposed to strip the words, as I am not getting any output. Here is the code:
string GetNextWord(string& s, size_t pos) {
string word;
char del = ' ';
int i = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] != del) {
word += s[i];
}
else break;
}
return word;
}
string StripWordsThatBeginWithLetter(string& s, char c) {
string result;
string word;
size_t pos = 0;
while (true)
{
word = GetNextWord(s, pos);
pos += word.size() + 1;
if (word.size() == 0)
{
break;
}
if (word[0] == c) {
size_t inx = 0;
inx = s.find(word[0]);
s.erase(inx, word.length());
}
else result = s;
}
return result;
}
Here's a hint. I'm guessing this is a homework problem. And I'm probably giving too much away.
std::string GetNextWord(const std::string &s, size_t pos)
{
std::string word;
// your code goes here to return a string that includes all the chars starting from s[pos] until the start of the next word (including trailing whitespace)
// return an empty string if at the end of the string
return word;
}
std::string StripWordsThatBeginWithLetter(const std::string& s, char c)
{
std::string result;
std::string word;
size_t pos = 0;
while (true)
{
word = GetNextWord(s, pos);
pos += word.size();
if (word.size() == 0)
{
break;
}
// your code on processing "word" goes here with respect
// to "c" goes here
}
return result;
}
Simple example in french. You are a gentleman and dont want to say "merde" too often, and so decided not to say any word starting with 'm'.
This program will help you :
"je suis beau merde je sais" becomes "je suis beau je sais"
#include <string>
#include <algorithm>
int main(){
std::string str ("je suis beau merde je le sais");
const auto forbiden_start ((const char) 'm');
std::cout << "initial rude string (word starting with \'" << forbiden_start << "\') : " << str << std::endl;
auto i (str.begin ());
auto wait (false);
std::for_each (str.begin (), str.end (), [&i, &forbiden_start, &wait] (const auto& c) {
if (wait) {
if (c == ' ') {
wait = false; return;
}
}
else {
if (c == forbiden_start) {
wait = true;
}
else *i++ = c;
}
});
if (i != str.end ()) str.erase (i, str.end ());
std::cout << "polite string : " << str << std::endl;
return 0;
}
All is not tested (separator is " "), but it is the idea

Logic for the string to not fall in the middle of another string

I need help in figuring out the logic or code to when I want my string not to fall in the middle of another string. For example my given word is "Birthday!" and the other string to look for it is "Happy Birthday Scott". It's going to return a false value because it's missing an exclamation point. Here is the code that I've worked
int Words::matchWords(const char* string, const char* sentence, int wordNum){
int wordCount = words(sentence); // the words function counts the number of words in the sentence
int strLength = strlen(str);
int sentLength = strlen(sentence);
int i = 0;
char strTemp[100];
char sentenceTemp[100];
strcpy(strTemp, str);
strcpy(sentenceTemp, sentence);
if (wordNum > wordCount) {
return false;
}
char* temp;
for (i = 0; i < strLength; i++) {
strTemp[i] = tolower(str[i]);
}
for (i = 0; i < sentLength; i++) {
sentenceTemp[i] = tolower(str[i]);
}
temp = strstr(sentenceTemp, strTemp);
if (temp != NULL) {
return true;
if (strTemp[i] != sentenceTemp[i]) {
return false;
}
else
return true;
}
else
return false;
}
Here is a super simple program for you to look at.
All you have to do for this problem is create your strings using std::string, determine if they are inside the big string using find(), and lastly check if it was found using string::npos.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string bday = "Birthday!";
string str1 = "Happy Birthday Scott";
int found1 = str1.find(bday);
string str2 = "Scott, Happy Birthday!";
int found2 = str2.find(bday);
if (found1 == string::npos) //if Birthday! is NOT found!
{
cout << "str1: " << "FALSE!" << endl;
}
if (found2 != string::npos) //if Birthday! IS found!
{
cout << "str2: " << "TRUE!" << endl;
}
}
Note that for string::npos, you use == for something NOT being found and != for something that IS found.

A better solution for comparing string patterns.?

Task : Create a function that returns true if two strings share the same letter pattern, and false otherwise.
I found a way to solve this task but I think it could be more simple and short. I converted all same letters to a specific char character for 2 strings. Then end of the process checked whether they are same or not. Any ideas for simpler solutions ?
#include <iostream>
#include <string>
using namespace std;
bool LetterPattern(string str1, string str2) {
// Controlling whether they have same size or not
if (str1.length() != str2.length()) {
return false;
}
else {
// Checking for ABC XYZ format type
int counter = 0;
for (int i = 0; i < str1.length()-1; i++) {
for (int k = i+1; k < str1.length(); k++) {
if (str1[i] == str1[k]) {
counter++;
}
}
}
int counter2 = 0;
for (int i = 0; i < str2.length() - 1; i++) {
for (int k = i + 1; k < str2.length(); k++) {
if (str2[i] == str2[k]) {
counter2++;
}
}
}
if (counter == 0 && counter2 == 0) {
return true;
}
// I added the above part because program below couldn't return 1 for completely different letter formats
// like XYZ ABC DEF etc.
//Converting same letters to same chars for str1
for (int i = 0; i < str1.length()-1; i++) {
for (int k = i+1; k < str1.length(); k++) {
if (str1[i] == str1[k]) {
str1[k] = (char)i;
}
}
str1[i] = (char)i;
}
}
//Converting same letters to same chars for str1
for (int i = 0; i < str2.length() - 1; i++) {
for (int k = i + 1; k < str2.length(); k++) {
if (str2[i] == str2[k]) {
str2[k] = (char)i;
}
}
str2[i] = (char)i;
}
if (str1 == str2) { // After converting strings, it checks whether they are same or not
return true;
}
else {
return false;
}
}
int main(){
cout << "Please enter two string variable: ";
string str1, str2;
cin >> str1 >> str2;
cout << "Same Letter Pattern: " << LetterPattern(str1, str2);
system("pause>0");
}
Examples:
str1
str2
result
AABB
CCDD
true
ABAB
CDCD
true
AAFFG
AAFGF
false
asdasd
qweqwe
true
As you want to see if one string is a Caesar cipher of the other, you might do:
bool LetterPatternImpl(const std::string& str1, const std::string& str2) {
if (str1.length() != str2.length()) { return false; }
std::array<std::optional<char>, 256> mapping; // char has limited range,
// else we might use std::map
for (std::size_t i = 0; i != str1.length(); ++i) {
auto index = static_cast<unsigned char>(str1[i]);
if (!mapping[index]) { mapping[index] = str2[i]; }
if (*mapping[index] != str2[i]) { return false; }
}
return true;
}
bool LetterPattern(const std::string& str1, const std::string& str2) {
// Both ways needed
// so ABC <-> ZZZ should return false.
return LetterPatternImpl(str1, str2) && LetterPatternImpl(str2, str1);
}
By 1 iteration on strings create key-value pairs That define corresponding characters.
In the second iteration check whether each character in the first/second string is compatible with the character with equal index in the second/second string. If there is no incompatibility return true, otherwise false.
First, as you did we can compare the size of 2 strings.
If they are equal we continue.
By iterating on 1 of the strings we can fill a map. Keys of the map are characters seen in the first string and its value is the corresponding character in the second string.
By reaching the nth character we check that whether we have a key or the same as this character or not.
If yes: Check the value that is equal to the nth character of the second string.
If no: we add a new key-value to the map. (the key is the nth character of the first string and the value is the nth character of the second string)
1.
After doing this we should do this again for another string. I mean for example if in the first step characters of the first string were keys, In the second step we should replace the string in the way that characters of second string become keys.
If both of them give true the answer is true. Otherwise false.
2.
Rather than replacing strings and repeat the iteration, we can prevent repetitive values to be added to the map.
To understand paragraph 1 and 2 imagine 1 iteration on strings of "ABC" and "ZZZ".
Notice that arrays can be used instead of map.
And, last but not least, an additional solution using "counting".
If we read the requirement, then you are only interested in a boolean result. That means, as soon as we have a 2nd association for a letter in the first string, then the result is false.
Example: If we have an 'a' and in the 2nd string at the same position a 'b', and then in some next position of the first string again an 'a' but then in the same position of the 2nd string a 'c', then we have 2 different associations for the letter a. And that is false.
If there is only one association per letter, then everything is ok.
How to accomplish "association" and "counting". For the "association, we will use an associative container, a std::unordered_map. And, we associate a letter from the first string, with a std::set of the already processed letters (from the 2nd string). The std::sets iinsert function will not add double letters from the secondt string. So, if there is again a 'b' associated with an 'a', that is completly fine.
But if there is a different associated letter, then the std::set will contain 2 elements. That is an indicator for a false result.
In such case, we stop evaluation characters immediately. This leads to a very compact and fast code.
Please see:
#include <iostream>
#include <string>
#include <unordered_map>
#include <utility>
#include <set>
bool letterPattern(const std::string& s1, const std::string& s2) {
// Here we will store the result of the function
bool result{ s1.length() == s2.length() };
// And here all associations
std::unordered_map<char, std::set<char>> association{};
// Add associations. Stop if result = false
for (size_t index{}; result && index < s1.length(); ++index)
if (const auto& [iter, ok] {association[s1[index]].insert(s2[index])}; ok)
result = association[s1[index]].size() == 1;
return result;
}
// Some driver test code
int main() {
std::vector<std::pair<std::string,std::string>> testData{
{"AABB", "CCDD"},
{"ABAB", "CDCD"},
{"AAFFG", "AAFGF"},
{"asdasd", "qweqwe"}
};
for (const auto& p : testData)
std::cout << std::boolalpha << letterPattern(p.first, p.second) << "\t for: '" << p.first << "' and '" << p.second << "'\n";
return 0;
}
Not sure about better, but a C++17 solution that builds a regular expression based on the first string's letters and matches it against the second:
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <tuple>
#include <regex>
bool match(const std::string &pattern, const std::string &s) {
std::unordered_map<char, int> indexes;
std::ostringstream builder;
int ref = 1;
for (char c : pattern) {
if (auto backref = indexes.find(c); backref != indexes.end()) {
builder << '\\' << backref->second;
} else {
if (ref > 1) {
builder << "(?!";
for (int n = 1; n < ref; n += 1) {
if (n != 1) {
builder << '|';
}
builder << '\\' << n;
}
builder << ')';
}
builder << "(.)";
indexes.emplace(c, ref++);
}
}
// std::cout << builder.str() << '\n';
return std::regex_match(s, std::regex{builder.str()});
}
int main() {
std::tuple<std::string, std::string, bool> tests[] = {
{"AABB", "CCDD", true},
{"ABAB", "CDCD", true},
{"AAFFG", "AAFGF", false},
{"asdasd", "qweqwe", true},
{"abc", "zzz", false}
};
std::cout << std::boolalpha;
for (const auto &[s1, s2, expected] : tests) {
if (match(s1, s2) == expected) {
std::cout << s1 << " => " << s2 << " = " << expected << ": PASS\n";
} else {
std::cout << s1 << " => " << s2 << " = " << (!expected) << ": FAIL\n";
}
}
return 0;
}
A simple (maybe not very efficient) approach:
#include<iostream>
#include<unordered_map>
using namespace std;
int main(void) {
string s1, s2;
unordered_map<string, char> subs;
cout<<"Enter the strings: ";
cin >> s1 >> s2;
if (s1.length() != s2.length())
cout<<"False"<<endl;
else {
for (int i=0; i<s1.length(); ++i) {
string key(1, s2[i]);
subs[key] = s1[i];
}
string s1_2 = "";
for (int i=0; i<s2.length(); ++i) {
string key(1, s2[i]);
s1_2 += subs[key];
}
if (s1 == s1_2)
cout<<"True"<<endl;
else
cout<<"False"<<endl;
}
return 0;
}
Time Complexity O(n); Space Complexity O(n)
If I understood right and:
AABB - CCDD = true
AAFFG - AAFGF = false
asdasd - qweqwe = true
That's not pattern, it's check if second string is result of encryption by substitution of first. You can do it in simpler way, by attempting to built substitution table. If it fails, i.e. there are more than one association between source and result, the outcome is false.
Simplest case is that we have to check whole string. If we would need to find that if any substring is substitution of pattern contained in second string, that squares the complexity:
#include <string>
#include <vector>
#include <map>
#include <optional>
#include <limits>
bool is_similar (const std::string& s1, const std::string& s2)
{
if(s1.length() != s2.length()) return false;
using TCh = std::decay_t<decltype(s1)>::value_type;
// for non-unicode characters can use an array
//std::optional<TCh> table[ std::numeric_limits<TCh>::max ];
// std::optional used for clarity, in reality may use `TCh`
// and compare with zero char
std::map< TCh, std::optional<TCh>> table;
for (size_t it = 0; it < s1.length(); ++it)
{
if( table[s1[it]].has_value() && table[s1[it]] != s2[it] ) return false;
if( table[s2[it]].has_value() && table[s2[it]] != s1[it] ) return false;
table[s1[it]] = s2[it];
//table[s2[it]] = s1[it]; if symmetric
}
return true;
}
If we find a new character, we will make it equal to the same position as the other string characters. Next time, if we found it again, we will check based on it.
Suppose we have 'aa' and 'cd'.
1st iteration: 'a'='c'
2nd iteration: already 'a'='c'(1st iteration), so we must need 'c' in our 2nd string.
But in our 2nd string, it is 'd'. so simply it will return false.
#include <bits/stdc++.h>
using namespace std;
// if you want to use map
bool LetterPattern_with_map(string str1,string str2)
{
if(str1.size()!=str2.size()) return false;
map<char,char> mp;
for(int i=0;i<str1.size();i++)
{
if(!mp[str1[i]]) { mp[str1[i]]=str2[i]; continue; }
if(mp[str1[i]]!=str2[i]) return false;
}
return true;
}
// if you want to use array instead of map
bool LetterPattern_with_array(string str1,string str2)
{
if(str1.size()!=str2.size()) return false;
int check[128]={0};
for(int i=0;i<str1.size();i++)
{
if(!check[str1[i]-'A'+1]) { check[str1[i]-'A'+1]=(int)(str2[i]-'A'+1); continue; }
if(check[str1[i]-'A'+1]!=(int)(str2[i]-'A'+1)) return false;
}
return true;
}
int main()
{
cout << "Please enter two string variable: ";
string str1, str2;
cin >> str1 >> str2;
cout << "Same Letter Pattern: " << LetterPattern_with_map(str1, str2)<<'\n';
cout << "Same Letter Pattern: " << LetterPattern_with_array(str1, str2);
}

Complex algorithm to extract numbers/number range from a string

I am working on a algorithm where I am trying the following output:
Given values/Inputs:
char *Var = "1-5,10,12,15-16,25-35,67,69,99-105";
int size = 29;
Here "1-5" depicts a range value, i.e. it will be understood as "1,2,3,4,5" while the values with just "," are individual values.
I was writing an algorithm where end output should be such that it will give complete range of output as:
int list[]=1,2,3,4,5,10,12,15,16,25,26,27,28,29,30,31,32,33,34,35,67,69,99,100,101,102,103,104,105;
If anyone is familiar with this issue then the help would be really appreciated.
Thanks in advance!
My initial code approach was as:
if(NULL != strchr((char *)grp_range, '-'))
{
int_u8 delims[] = "-";
result = (int_u8 *)strtok((char *)grp_range, (char *)delims);
if(NULL != result)
{
start_index = strtol((char*)result, (char **)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
while(NULL != result)
{
end_index = strtol((char*)result, (char**)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
while(start_index <= end_index)
{
grp_list[i++] = start_index;
start_index++;
}
}
else if(NULL != strchr((char *)grp_range, ','))
{
int_u8 delims[] = ",";
result = (unison_u8 *)strtok((char *)grp_range, (char *)delims);
while(result != NULL)
{
grp_list[i++] = strtol((char*)result, (char**)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
}
But it only works if I have either "0-5" or "0,10,15". I am looking forward to make it more versatile.
Here is a C++ solution for you to study.
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int ConvertString2Int(const string& str)
{
stringstream ss(str);
int x;
if (! (ss >> x))
{
cerr << "Error converting " << str << " to integer" << endl;
abort();
}
return x;
}
vector<string> SplitStringToArray(const string& str, char splitter)
{
vector<string> tokens;
stringstream ss(str);
string temp;
while (getline(ss, temp, splitter)) // split into new "lines" based on character
{
tokens.push_back(temp);
}
return tokens;
}
vector<int> ParseData(const string& data)
{
vector<string> tokens = SplitStringToArray(data, ',');
vector<int> result;
for (vector<string>::const_iterator it = tokens.begin(), end_it = tokens.end(); it != end_it; ++it)
{
const string& token = *it;
vector<string> range = SplitStringToArray(token, '-');
if (range.size() == 1)
{
result.push_back(ConvertString2Int(range[0]));
}
else if (range.size() == 2)
{
int start = ConvertString2Int(range[0]);
int stop = ConvertString2Int(range[1]);
for (int i = start; i <= stop; i++)
{
result.push_back(i);
}
}
else
{
cerr << "Error parsing token " << token << endl;
abort();
}
}
return result;
}
int main()
{
vector<int> result = ParseData("1-5,10,12,15-16,25-35,67,69,99-105");
for (vector<int>::const_iterator it = result.begin(), end_it = result.end(); it != end_it; ++it)
{
cout << *it << " ";
}
cout << endl;
}
Live example
http://ideone.com/2W99Tt
This is my boost approach :
This won't give you array of ints, instead a vector of ints
Algorithm used: (nothing new)
Split string using ,
Split the individual string using -
Make a range low and high
Push it into vector with help of this range
Code:-
#include<iostream>
#include<vector>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
int main(){
std::string line("1-5,10,12,15-16,25-35,67,69,99-105");
std::vector<std::string> strs,r;
std::vector<int> v;
int low,high,i;
boost::split(strs,line,boost::is_any_of(","));
for (auto it:strs)
{
boost::split(r,it,boost::is_any_of("-"));
auto x = r.begin();
low = high =boost::lexical_cast<int>(r[0]);
x++;
if(x!=r.end())
high = boost::lexical_cast<int>(r[1]);
for(i=low;i<=high;++i)
v.push_back(i);
}
for(auto x:v)
std::cout<<x<<" ";
return 0;
}
You're issue seems to be misunderstanding how strtok works. Have a look at this.
#include <string.h>
#include <stdio.h>
int main()
{
int i, j;
char delims[] = " ,";
char str[] = "1-5,6,7";
char *tok;
char tmp[256];
int rstart, rend;
tok = strtok(str, delims);
while(tok != NULL) {
for(i = 0; i < strlen(tok); ++i) {
//// range
if(i != 0 && tok[i] == '-') {
strncpy(tmp, tok, i);
rstart = atoi(tmp);
strcpy(tmp, tok + i + 1);
rend = atoi(tmp);
for(j = rstart; j <= rend; ++j)
printf("%d\n", j);
i = strlen(tok) + 1;
}
else if(strchr(tok, '-') == NULL)
printf("%s\n", tok);
}
tok = strtok(NULL, delims);
}
return 0;
}
Don't search. Just go through the text one character at a time. As long as you're seeing digits, accumulate them into a value. If the digits are followed by a - then you're looking at a range, and need to parse the next set of digits to get the upper bound of the range and put all the values into your list. If the value is not followed by a - then you've got a single value; put it into your list.
Stop and think about it: what you actually have is a comma
separated list of ranges, where a range can be either a single
number, or a pair of numbers separated by a '-'. So you
probably want to loop over the ranges, using recursive descent
for the parsing. (This sort of thing is best handled by an
istream, so that's what I'll use.)
std::vector<int> results;
std::istringstream parser( std::string( var ) );
processRange( results, parser );
while ( isSeparator( parser, ',' ) ) {
processRange( results, parser );
}
with:
bool
isSeparator( std::istream& source, char separ )
{
char next;
source >> next;
if ( source && next != separ ) {
source.putback( next );
}
return source && next == separ;
}
and
void
processRange( std::vector<int>& results, std::istream& source )
{
int first = 0;
source >> first;
int last = first;
if ( isSeparator( source, '-' ) ) {
source >> last;
}
if ( last < first ) {
source.setstate( std::ios_base::failbit );
}
if ( source ) {
while ( first != last ) {
results.push_back( first );
++ first;
}
results.push_back( first );
}
}
The isSeparator function will, in fact, probably be useful in
other projects in the future, and should be kept in your
toolbox.
First divide whole string into numbers and ranges (using strtok() with "," delimiter), save strings in array, then, search through array looking for "-", if it present than use sscanf() with "%d-%d" format, else use sscanf with single "%d" format.
Function usage is easily googling.
One approach:
You need a parser that identifies 3 kinds of tokens: ',', '-', and numbers. That raises the level of abstraction so that you are operating at a level above characters.
Then you can parse your token stream to create a list of ranges and constants.
Then you can parse that list to convert the ranges into constants.
Some code that does part of the job:
#include <stdio.h>
// Prints a comma after the last digit. You will need to fix that up.
void print(int a, int b) {
for (int i = a; i <= b; ++i) {
printf("%d, ", i);
}
}
int main() {
enum { DASH, COMMA, NUMBER };
struct token {
int type;
int value;
};
// Sample input stream. Notice the sentinel comma at the end.
// 1-5,10,
struct token tokStream[] = {
{ NUMBER, 1 },
{ DASH, 0 },
{ NUMBER, 5 },
{ COMMA, 0 },
{ NUMBER, 10 },
{ COMMA, 0 } };
// This parser assumes well formed input. You have to add all the error
// checking yourself.
size_t i = 0;
while (i < sizeof(tokStream)/sizeof(struct token)) {
if (tokStream[i+1].type == COMMA) {
print(tokStream[i].value, tokStream[i].value);
i += 2; // skip to next number
}
else { // DASH
print(tokStream[i].value, tokStream[i+2].value);
i += 4; // skip to next number
}
}
return 0;
}

Strings with whitespace in a list?

I have this function sentanceParse with a string input which returns a list. The input might be something like "Hello my name is Anton. What's your name?" and then the return value would be a list containing "Hello my name is Anton" and "What's your name?". However, this is not what happens. It seems as if the whitespaces in the sentences are treated like a separator and therefore the return is rather "Hello", "my", "name" etc instead of what I expected.
How would you propose I solve this?
As I am not a 100% sure the problem does not lie within my code, I will add that to the post as well:
Main:
list<string> mylist = sentanceParse(textCipher);
list<string>::iterator it;
for(it = mylist.begin(); it != mylist.end(); it++){
textCipher = *it;
cout << textCipher << endl; //This prints out the words separately instead of the entire sentances.
sentanceParse:
list<string> sentanceParse(string strParse){
list<string> strList;
int len = strParse.length();
int pos = 0;
int count = 0;
for(int i = 0; i < len; i++){
if(strParse.at(i) == '.' || strParse.at(i) == '!' || strParse.at(i) == '?'){
if(i < strParse.length() - 1){
while(i < strParse.length() - 1 && (strParse.at(i+1) == '.' || strParse.at(i+1) == '!' || strParse.at(i+1) == '?')){
if(strParse.at(i+1) == '?'){
strParse.replace(i, 1, "?");
}
strParse.erase(i+1, 1);
len -= 1;
}
}
char strTemp[2000];
int lenTemp = strParse.copy(strTemp, i - pos + 1, pos);
strTemp[lenTemp] = '\0';
std::string strAdd(strTemp);
strList.push_back(strAdd);
pos = i + 1;
count ++;
}
}
if(count == 0){
strList.push_back(strParse);
}
return strList;
}
Your implementation of sentence parse is wrong, here is a simpler correct solution.
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
EDIT:
Here is a full example program using this function. Type some sentences in your console, press enter and it will spit the sentences out with a newline separating them instead of punctuation.
#include <iostream>
#include <string>
#include <list>
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
int main (int argc, const char * argv[])
{
std::string s;
while (std::getline(std::cin,s)) {
std::list<std::string> t= sentence_parse(s);
std::list<std::string>::iterator x=t.begin();
while (x!=t.end()) {
std::cout<<*x<<"\n";
++x;
}
}
return 0;
}
// This function should be easy to adapt to any basic libary
// this is in Windows MFC
// pass in a string, a char and a stringarray
// returns an array of strings using char as the separator
void tokenizeString(CString theString, TCHAR theToken, CStringArray *theParameters)
{
CString temp = "";
int i = 0;
for(i = 0; i < theString.GetLength(); i++ )
{
if (theString.GetAt(i) != theToken)
{
temp += theString.GetAt(i);
}
else
{
theParameters->Add(temp);
temp = "";
}
if(i == theString.GetLength()-1)
theParameters->Add(temp);
}
}