Related
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);
}
sorry for such a stupid question but I couldn't find any obvious answer.
I need to read from stdin first an int n with the size of an array, and then integer values from a string in the format "1 2 3 4 5 6" with n elements.
If I knew the number of parameters at compile time I could use something like a scanf (or the safe alternatives) with a format string like "%d %d %d %d %d %d", but here I will only know that value at run time.
What would be the best way to do this in C++? Performance is important but more than that safety.
How should I read a format string of variable length in C++ from stdin?
You should not attempt to do such thing. Only ever use constant format strings.
I need to read from stdin first an int n with the size of an array, and then integer values
What would be the best way to do this in C++?
Read one value at a time. Repeat using a loop.
Here's a function that does what errorika describes:
const int SIZE = //as much of your memory as you'd like the user to have access to
***caller function must include this:
//allocate a string to hold some data;
char* buffer = NULL;
buffer = malloc (SIZE * sizeof(char));
if (buffer == NULL) {
printf("malloc error terminating\n");
return;
}
***
void getEntry(char* buffer) {
int count = 0;
int maxlen = SIZE - 1;
char a = '0';
for (int i = 0; i < SIZE; i++) {
buffer[i] = '0';
}
while (a != '\n' && count < maxlen) {
a = fgetc(stdin);
buffer[count] = a;
count++;
}
if (a == '\n') {
buffer[count - 1] = '\0';
}
else {
buffer[count] = '\0';
do {
a = fgetc(stdin);
} while (a != '\n');
}
}
This is all basic C code but user entry is evil. Here is what I've come up with for more C++ idiomatic user input functions (query is just the message string you pass in):
template<typename T>
void getInput(const std::string query, T& entry) {
std::string input;
std::cout << query << std::endl;
getline(std::cin, input);
std::stringstream buffer{input};
buffer >> entry;
}
OR
template<typename T>
void getInput2(std::string query, T& entry) {
bool validInput = false;
while (validInput == false)
{
validInput = true;
std::cout << query << std::endl;
std::cin >> entry;
if (std::cin.fail()) {
validInput = false;
std::cout << "Unacceptable entry\n" << std::endl;
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
I'm trying to figure out how to can fold a word from a string. For example "code" after the folding would become "ceod". Basically start from the first character and then get the last one, then the second character. I know the first step is to start from a loop, but I have no idea how to get the last character after that. Any help would be great. Heres my code.
#include <iostream>
using namespace std;
int main () {
string fold;
cout << "Enter a word: ";
cin >> fold;
string temp;
string backwards;
string wrap;
for (unsigned int i = 0; i < fold.length(); i++){
temp = temp + fold[i];
}
backwards= string(temp.rbegin(),temp.rend());
for(unsigned int i = 0; i < temp.length(); i++) {
wrap = fold.replace(backwards[i]);
}
cout << wrap;
}
Thanks
#Supreme, there are number of ways to do your task and I'm going to post one of them. But as #John had pointed you must try your own to get it done because real programming is all about practicing a lot. Use this solution just as a reference of one possibility and find many others.
int main()
{
string in;
cout <<"enter: "; cin >> in;
string fold;
for (int i=0, j=in.length()-1; i<in.length()/2; i++, j--)
{
fold += in[i];
fold += in[j];
}
if( in.length()%2 != 0) // if string lenght is odd, pick the middle
fold += in[in.length()/2];
cout << endl << fold ;
return 0;
}
good luck !
There are two approaches to this form of problem, a mathematically exact method would be to create a generator function which returns the number in the correct order.
An easier plan would be to modify the string to solve practically the problem.
Mathematical solution
We want a function which returns the index in the string to add. We have 2 sequences - increasing and decreasing and they are interleaved.
sequence 1 :
0, 1 , 2, 3.
sequence 2
len-1, len-2, len-3, len-4.
Given they are interleaved, we want even values to be from sequence 1 and odd values from sequence 2.
So our solution would be to for a given new index, choose which sequence to use, and then return the next value from that sequence.
int generator( int idx, int len )
{
ASSERT( idx < len );
if( idx %2 == 0 ) { // even - first sequence
return idx/2;
} else {
return (len- (1 + idx/2);
}
}
This can then be called from a function fold...
std::string fold(const char * src)
{
std::string result;
std::string source(src);
for (size_t i = 0; i < source.length(); i++) {
result += source.at(generator(i, source.length()));
}
return result;
}
Pratical solution
Although less efficient, this can be easier to think about. We are taking either the first or the last character of a string. This we will do using string manipulation to get the right result.
std::string fold2(const char * src)
{
std::string source = src;
enum whereToTake { fromStart, fromEnd };
std::string result;
enum whereToTake next = fromStart;
while (source.length() > 0) {
if (next == fromStart) {
result += source.at(0);
source = source.substr(1);
next = fromEnd;
}
else {
result += source.at(source.length() - 1); // last char
source = source.substr(0, source.length() - 1); // eat last char
next = fromStart;
}
}
return result;
}
You can take advantage of the concept of reverse iterators to write a generic algorithm based on the solution presented in Usman Riaz answer.
Compose your string picking chars from both the ends of the original string. When you reach the center, add the char in the middle if the number of chars is odd.
Here is a possible implementation:
#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
template <class ForwardIt, class OutputIt>
OutputIt fold(ForwardIt source, ForwardIt end, OutputIt output)
{
auto reverse_source = std::reverse_iterator<ForwardIt>(end);
auto reverse_source_end = std::reverse_iterator<ForwardIt>(source);
auto source_end = std::next(source, std::distance(source, end) / 2);
while ( source != source_end )
{
*output++ = *source++;
*output++ = *reverse_source++;
}
if ( source != reverse_source.base() )
{
*output++ = *source;
}
return output;
}
int main() {
std::vector<std::pair<std::string, std::string>> tests {
{"", ""}, {"a", "a"}, {"stack", "sktca"}, {"steack", "sktcea"}
};
for ( auto const &test : tests )
{
std::string result;
fold(
std::begin(test.first), std::end(test.first),
std::back_inserter(result)
);
std::cout << (result == test.second ? " OK " : "FAILED: ")
<< '\"' << test.first << "\" --> \"" << result << "\"\n";
}
}
Here is the code:
string msg;
niceArray = txtReader("chatTest/replaces.txt");
vector<vector<string>>vMaster;
if(vMaster.size() <1){
string arr[] = { "a","A","á","#","à ","â","ã","ÃÃ","€Ã","ƒÃ"};
vector<string> tempA(arr, arr+4);
vMaster.push_back(tempA);//"aAá#à âãÃÀÃÂ"
}
string ex;
while(sstr.good()){
sstr>>ex;
vectorCheck.push_back(ex);
}
for(int e = 0; e < vectorCheck.size(); e=e+1){
//if(e > vectorCheck[e].length()) break;
auto str = vectorCheck[e];
for(int b = 0; b < vMaster.size(); b=b+1){
for(int j=0; vMaster[b].size(); j=j+1){
//int f = str.find(vMaster[b][j]);
if(str.find(vMaster[b][j]) != std::string::npos){
int f = str.find(vMaster[b][j]);
//if(vMaster[b][j].length() > 1){
str.replace(f,2,vMaster[b][0]);
//break;
// }
//
}
}
}
for(int i = 0; i < xingArray.size(); i=i+1){
if(str == xingArray[i]){
vectorCheck[e] = niceArray[rand() % niceArray.size()];
}
}
}
So for each sentence i type i am checking each word and looking if there is any of that string arr characteres in it, if there is i replace it for the vector[0] in this case "a".
The problem is that this line str.find(vMaster[b][j]) != std::string::npos never returns me -1... Even when i type like "c" in finds c in there or "f" or any word and i get an error. The funny stuff is that when i type like "á" that turns into "Ã|" it works and with the "ã" that turns into "ã" it doesnt give me 0 again... I really dont know whats going on... I really tried hard here and if anyone has any opinion i would like to hear thanks.
std::string str ("potato.");
std::string str2 ("ta");
std::size_t found = str.find(str2);
if ( found != std::string::npos)
std::cout << "first 'ta' found at: " << found << '\n';
str.replace( found, 2, "");
I dont think C++ has any single method to find and replace so why not use inbuilt find and then replace.
For example:
void find_and_replace(string& source, string const& find, string const& replace)
{
for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
{
source.replace(i, find.length(), replace);
i += replace.length() - find.length() + 1;
}
}
int main()
{
string text = "potato";
find_and_replace(text, "ta", "");
cout << "After one replacement: " << text << endl;
find_and_replace(text, "te", "");
cout << "After another replacement: " << text << endl;
return 0;
}
What I want to do is convert a string such as
"a4b2f0" into "f0b2a4"
or in more simple terms:
turning "12345678" into "78563412"
The string will always have an even number of characters so it will always divide by 2. I'm not really sure where to start.
One simple way to do that is this:
std::string input = "12345678";
std::string output = input;
std::reverse(output.begin(), output.end());
for(size_t i = 1 ; i < output.size(); i+=2)
std::swap(output[i-1], output[i]);
std::cout << output << std::endl;
Online demo
A bit better in terms of speed, as the previous one swaps elements twice, and this one swap each pair once:
std::string input = "12345678";
std::string output = input;
for(size_t i = 0, middle = output.size()/2, size = output.size(); i < middle ; i+=2 )
{
std::swap(output[i], output[size - i- 2]);
std::swap(output[i+1], output[size -i - 1]);
}
std::cout << output << std::endl;
Demo
Let's get esoteric... (not tested! :( And definitely not built to handle odd-length sequences.)
typedef <typename I>
struct backward_pair_iterator {
typedef I base_t;
base_t base;
bool parity;
backward_pair_iterator(base_t base, parity = false):
base(base), parity(parity) {
++base;
}
backward_pair_iterator operator++() {
backward_pair_iterator result(base, !parity);
if (parity) { result.base++; result.base++; }
else { result.base--; }
return result;
}
};
template <typename I>
backward_pair_iterator<I> make_bpi(I base) {
return backward_pair_iterator<I>(base);
}
std::string output(make_bpi(input.rbegin()), make_bpi(input.rend()));
static string reverse(string entry) {
if (entry.size() == 0) {
return "";
} else {
return entry.substr (entry.size() - 2, entry.size()) + reverse(entry.substr (0, entry.size() - 2));
}
}
My method uses the power of recursive programming
A simple solution is this:
string input = "12345678";
string output = "";
for(int i = input.length() - 1; i >= 0; i-2)
{
if(i -1 >= 0){
output += input[i -1];
output += input[i];
}
}
Note: You should check to see if the length of the string when mod 2 is = because otherwise this will go off the end. Do something like I did above.