How to check if two strings have any common part (sub-string) in c++.
I thought of sorting the strings and then using loops on both the strings to check if they have a common part. But I am not sure. And I also thought that this would not be the optimal strategy for the question
Consider strings - 'batman' and 'catman'
They have a common part 'atman'.
P.S. I am not checking for more than one characters. eg - 'apple' and 'batman' have a in common but I am interested in sub-string (min. two common consecutive characters).
This might not be the most efficient code, but so you can get an idea of how it would work in the batman and catman case:
Note: This does solve other cases like "batmanuc" and "catmanic", which would be "atman".
It is not perfect nor the most efficient, but it can help you understand a way to do it and how to manage positions of arrays. From here, implementing it in your own way and adding details is up to you!
like if max>2then print the array, if not, dont print it, for example.
#include <iostream>
using namespace std;
int main()
{
char arr1[10]="batmanuc";
char arr2[10]="catmanic";
char common[10];
int cont=0;
int max=0;
for (int i=0;i<10;i++){
if(arr1[i]==arr2[i]){
if(cont==max){
common[cont]=arr1[i];
cont++;
max++;
}
}
else cont=0;
}
printf("%s",common);
return 0;
}
A stupid algorithm - for each window size from a maximum window (equal to the max of string lengths) to minimum window size (equal to 2, stated in the question), for each position in both strings compare each position with each position in both string with each window_size.
#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
#include <cstdio>
std::string find_common_part(std::string one, std::string two) {
const auto onesize = one.size();
const auto twosize = two.size();
const auto onebegin = one.begin();
const auto twobegin = two.begin();
// min. two common consecutive characters
for (std::size_t window_size = std::max(onesize, twosize);
window_size >= 2;
--window_size) {
for (auto onepos = onebegin,
oneposmax = onebegin + (onesize - window_size);
onepos <= oneposmax;
++onepos) {
for (auto twopos = twobegin,
twoposmax = twobegin + (twosize - window_size);
twopos <= twoposmax;
++twopos) {
if (std::equal(onepos, onepos + window_size, twopos)) {
return std::string(onepos, onepos + window_size);
}
}
}
}
return std::string();
}
int main()
{
std::cout << find_common_part("catman", "batman") << std::endl;
assert(find_common_part("catman", "batman") == "atman");
assert(find_common_part("batman", "manbat") == "man" ||
find_common_part("batman", "manbat") == "bat");
return 0;
}
This function will give you the longest common substring of two strings: (Probably not the fastest way you would do it)
std::string GetLongestCommonSubstring(std::string const& a, std::string const& b) {
std::vector<std::string> substrings;
for (auto beg = a.begin(); beg != std::prev(a.end()); ++beg)
for (auto end = beg; end != a.end(); ++end)
if (b.find(std::string(beg, std::next(end))) != std::string::npos)
substrings.emplace_back(beg, std::next(end));
return *std::max_element(substrings.begin(), substrings.end(),
[](auto& elem1, auto& elem2) { return elem1.length() < elem2.length(); });
}
Example:
int main() {
std::cout << GetLongestCommonSubstring("batman", "catman") << std::endl;
}
Output:
atman
Related
I am sure this code isn't perfect, but I am new to programming and am trying to work out a challenge for checking if a number is a palindrome or not. I tried writing a bool-type function in the code to return 'true' if the number is a palindrome and 'false' otherwise.
Anyway, jumping to context, I want this code to print 'YES" every time the computer notices a sign of palindrome-ism. The code is compiling successfully, however, it does not output anything after 'What is the integer you wanna check palindromism for?:' even when inputting numbers like '12321', '111', '1234321' (palindromes).
Can anyone help me, and if possible, without changing much of the code tell me ways to achieve what I want to (to prove palindrome-ism)?
#include <cstring>
using namespace std;
bool isPalindrome(int x, string md) {
int y = md.length() + 1;
char abz[y];
for (int i=0; i < md.length()-1; ++i) {
if (abz[i] == (md.length()-1)-i){
cout << "YES";
}
}
return true;
}
int main(){
int x;
cout << "What is the integer you wanna check palindromism for?: ";
cin >> x;
string md = to_string(x);
isPalindrome(x, md);
return 0;
}
Thanks!
I'm not sure what you're trying to do in isPalindrome.
One way to check if a string of size len is palindrome is to compare its i-th and (len-i-1)-th characters for i ranging in [0, len / 2).
If they differ at any point the string is not palindrome.
Here's how you may do it:
bool isPalindrome(std::string const& md) {
if (md.empty()) // Empty strings are not palindrome
return false;
auto const len = md.size();
auto const halfLen = len / 2;
for (std::size_t i = 0; i != halfLen; ++i)
if (md[i] != md[len - i - 1])
return false;
return true;
}
Can anyone help me, and if possible, without changing much of the code tell
me ways to achieve what I want to (to prove palindrome-ism)?
Please check the comments I've added in the code:
// Include the correct headers
#include <iostream>
#include <string>
// Don't do this
//using namespace std;
// This just checks if the string is palindrome.
// It does not print anything.
bool isPalindrome(std::string const& md) {
if (md.empty())
return false;
auto const len = md.size();
auto const halfLen = len / 2;
for (std::size_t i = 0; i != halfLen; ++i)
if (md[i] != md[len - i - 1])
return false;
return true;
}
int main() {
// No need to parse the number and convert it to string again.
//int x;
std::string md;
std::cout << "What is the integer you wanna check palindromism for?: ";
// NOTE: you may input any string here: not just integers.
std::cin >> md;
std::cout << (isPalindrome(md) ? "YES" : "") << '\n';
// ^ print "YES" or nothing
return 0;
}
You may also implement isPalindrome with algorithms and iterators like so:
// You'll need these two headers
#include <algorithm>
#include <iterator>
template <typename BidIt>
bool isPalindrome(BidIt first, BidIt last) {
if (first == last)
return false;
auto const halfLength = std::distance(first, last);
auto const mid = std::next(first, halfLength);
auto const rFirst = std::make_reverse_iterator(last);
return std::equal(first, mid, rFirst);
}
bool isPalindrome(std::string const& str) {
return isPalindrome(std::cbegin(str), std::cend(str));
}
This is basically the same algorithm as above but you can reuse
template <typename BidIt>
bool isPalindrome(BidIt, BidIt);
with more containers than just std::string.
My asssignmet is:
Input three strings into an array of strings. First, use a function to find the length for the end comparisons, then again compare the last two characters separately.
Second, use a string function to take a sub-string of the first three characters for the beginning comparison all at once.
{
string stringarray[3] = {"yankee", "yes", "word"};
for (int x = 0; x < 3; x++)
string substringend1 = stringarray.substr(stringarray.length(stringarray[x]) - 2, stringarray.length(stringarray[x]));
string substringend2 = stringarray.substr(stringarray.length(stringarray[x]) - 1, stringarray.length(stringarray[x]));
string substringstart = stringarray.substr(0, 3);
if (substringend1 == "e" && substringend2 == "s" || substringstart == "yan") {
for (int y = 0; y < 3; y++)
cout << stringarray[y];
}
}
I know Im an idiot and this is a bad question format and whatever but I need help
C++20 makes this very easy:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
std::vector<std::string> in{"yankee", "yes", "word"};
for(const auto& s : in)
{
if(s.starts_with("yan") || s.ends_with("es"))
std::cout << s << '\n';
}
return 0;
}
Live Example: https://godbolt.org/z/6Eq3nnKrf
The hardest part is finding C++20 support in your compiler. Later versions of gcc have it, latest versions of MSVC have it.
Without C++20, you need to find replacements for starts_with() and ends_with(). Starts with is easy and has a good answer here: How do I check if a C++ std::string starts with a certain string, and convert a substring to an int?
Using that answer we can write a pretty simple starts_with() function:
bool starts_with(const std::string& in, const std::string& prefix)
{
return in.rfind(prefix, 0) == 0;
}
We can use that as an example on how to create an ends_with() function too!
bool ends_with(const std::string& in, const std::string& prefix)
{
return in.rfind(prefix) == (in.size() - prefix.size());
}
Then our main code changes only slightly:
int main()
{
std::vector<std::string> in{"yankee", "yes", "word"};
for(const auto& s : in)
{
if(starts_with(s, "yan") || ends_with(s, "es"))
std::cout << s << '\n';
}
return 0;
}
You can use std::compare to compare the start of a string with yan or its end with es:
#include <iostream>
#include <string>
using namespace std;
bool startWithString(string a, string mod)
{
if (a.length() < mod.length()) {return false;}
return 0 == a.compare(0, mod.length(), mod);
}
bool endWithString(string a, string mod)
{
if (a.length() < mod.length()) {return false;}
return 0 == a.compare(a.length()-mod.length(), mod.length(), mod);
}
int main()
{
string str[4] = {"yankee", "yes", "word", "yankes"};
for (int i = 0; i < 4; i++)
{
string cur = str[i];
if (startWithString(cur, "yan") || endWithString(cur, "es")) {cout << cur << endl;}
}
}
Result :
yankee
yes
yankes
Using rfind() also yields similar result:
bool startWithString(string a, string mod)
{
return a.rfind(mod, 0) == 0;
}
bool endWithString(string a, string mod)
{
return a.rfind(mod) == (a.length() - mod.length());
}
As #Chad mentioned, C++20 also support starts_with() and ends_with(), which makes stuff easier.
So I was given the question:
Find ALL of the non-repeating characters in a given string;
After doing some Google searching it was clear to me that finding the first non repeating character was pretty common. I found many examples of how to do that, but I have not really found anything on how to find ALL of the non repeating characters instead of just the first one.
my example code so far is:
#include <iostream>
#include <unordered_map>
using namespace std;
char findAllNonRepeating(const string& s) {
unordered_map<char, int> m;
for (unsigned i = 0; i < s.length(); ++i) {
char c = tolower(s[i]);
if (m.find(c) == m.end())
m[c] = 1;
else
++m[c];
}
auto best = m.begin();
for (auto it = m.begin(); it != m.end(); ++it)
if (it->second <= best->second)
best = it;
return (best->first);
}
int main()
{
cout << findAllNonRepeating("dontknowwhattochangetofindallnonrepeatingcharacters") << endl;
}
I am not sure what I need to change or add to have this find all of the non repeating characters.
k, f, p, s should be the non repeating characters in this string.
Any hints or ideas are greatly appreciated!
As suggested, simply keep a frequency map. Then, once the string is processed, iterate over the map, returning only those values that occur exactly once.
#include <iostream>
#include <map>
#include <vector>
using namespace std;
std::vector<char> nonRepeating(const std::string& s)
{
std::map<char, int> frequency;
for(int i=0;i<s.size();i++)
{
frequency[s[i]]++;
}
std::vector<char> out;
for(auto it = frequency.begin(); it != frequency.end(); it++)
{
if(it->second == 1)
out.push_back(it->first);
}
return out;
}
int main() {
// your code goes here
std::string str = "LoremIpsum";
for(char c : nonRepeating(str))
{
std::cout << c << std::endl;
}
return 0;
}
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";
}
}
I have a directory containing files {"good_6", good_7", "good_8"...,"good_660"}, after reading it using readdir and storing in a vector I get {"good_10", "good_100", "good_101", "good_102"...}.
What I want to do is to keep the file names as {"good_6", good_7", "good_8"...,"good_660"} in the vector and then replacing first name with 1, second with 2 and so on... such that good_6 will be 1, good_7 will be 2 and so on. but now good_10 corresponds to 1 and good_100 to 2 and so on.
I tried std::sort on vector but the values are already sorted, just not in a way that I desire (based on integer after _). Even if I just get the last integer and sort on that, it will still be sorted as 1, 100, 101...
Any help would be appreciated. Thanks.
You can use a custom function that compares strings with a special case for digits:
#include <ctype.h>
int natural_string_cmp(const char *sa, const char *sb) {
for (;;) {
int a = (unsigned char)*sa++;
int b = (unsigned char)*sb++;
/* simplistic version with overflow issues */
if (isdigit(a) && isdigit(b)) {
const char *sa1 = sa - 1;
const char *sb1 = sb - 1;
unsigned long na = strtoul(sa1, (char **)&sa, 10);
unsigned long nb = strtoul(sb1, (char **)&sb, 10);
if (na == nb) {
if ((sa - sa1) == (sb - sb1)) {
/* XXX should check for '.' */
continue;
} else {
/* Perform regular strcmp to handle 0 :: 00 */
return strcmp(sa1, sb1);
}
} else {
return (na < nb) ? -1 : +1;
}
} else {
if (a == b) {
if (a != '\0')
continue;
else
return 0;
} else {
return (a < b) ? -1 : 1;
}
}
}
}
Depending on your sorting algorithm, you may need to wrap it with an extra level of indirection:
int natural_string_cmp_ind(const void *p1, const void *p2) {
return natural_string_cmp(*(const char * const *)p1, *(const char * const *)p2);
}
char *array[size];
... // array is initialized with filenames
qsort(array, size, sizeof(*array), natural_string_cmp_ind);
I think you can play around with your data structure. For example instead of vector<string>, you can convert your data to vector< pair<int, string> >. Then {"good_6", "good_7", "good_8"...,"good_660"} should be {(6, "good"), (7, "good"), (7, "good")..., (660, "good")}. In the end, you convert it back and do whatever you want.
Another way is just to define your own comparator to do the exact comparison as what you want.
You can use string::replace to replace string "good_" with empty string, and use stoi to convert the rest of the integral part of the string. Lets say the value obtained is x.
Create std::map and populate it in this way myMap[x] = vec_element.
Then you can traverse from m.begin() till m.end() to find sorted order.
Code:
myMap[ stoi( vec[i].replace(0,5,"") )] = vec[i];
for( MapType::iterator it = myMap.begin(); it != myMap.end(); ++it ) {
sortedVec.push_back( it->second );
If I understand your question, you're just having trouble with the sorting and not how you plan to change the names after you sort.
Something like this might work for you:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <tuple>
#include <string.h>
int main()
{
std::vector<std::string> v;
char buffer[64] = {};
for (size_t i = 1; i < 10; ++i)
{
sprintf(buffer, "good_%d", i * 3);
v.push_back(buffer);
sprintf(buffer, "bad_%d", i * 2);
v.push_back(buffer);
}
std::random_shuffle(v.begin(), v.end());
for (const auto& s : v)
{
std::cout << s << "\n";
}
std::sort(v.begin(), v.end(),
[](const std::string& lhs, const std::string& rhs)
{
//This assumes a lot about the contents of the strings
//and has no error checking just to keep things short.
size_t l_pos = lhs.find('_');
size_t r_pos = rhs.find('_');
std::string l_str = lhs.substr(0, l_pos);
std::string r_str = rhs.substr(0, r_pos);
int l_num = std::stoi(lhs.substr(l_pos + 1));
int r_num = std::stoi(rhs.substr(r_pos + 1));
return std::tie(l_str, l_num) < std::tie(r_str, r_num);
});
std::cout << "-----\n";
for (const auto& s : v)
{
std::cout << s << "\n";
}
return 0;
}
Managed to do it with the following compare function:
bool numericStringComapre(const std::string& s1, const std::string& s2)
{
size_t foundUnderScore = s1.find_last_of("_");
size_t foundDot = s1.find_last_of(".");
string s11 = s1.substr(foundUnderScore+1, foundDot - foundUnderScore - 1);
foundUnderScore = s2.find_last_of("_");
foundDot = s2.find_last_of(".");
string s22 = s2.substr(foundUnderScore+1, foundDot-foundUnderScore - 1);
int i1 = stoi(s11);
int i2 = stoi(s22);
if (i1 < i2) return true;
return false;
}
full file name was good_0.png, hence that find_last_of(".").