I offer this code as something to look forward to be able to write in the future, not as something I would want you to come up with right now. What you have is great for someone at your level of C++.
The most important aspect of this code is its use of set
. set
is a container that keeps its element ordered, even as you add more element to it. It's contents will always kept sorted. By default, set
will perform it's duties by comparing elements with a plain less-than comparator, i.e. <
. So, by default, a set<int>
will keep a bunch of int
s ordered from the smallest to the greatest.
Though that's the default, you can change the comparison function. The standard library already has some options ready for you to use. For example, to keep the int
s sorted from greatest to smallest, use set<int, greater<>>
instead of just set<int>
.
But what we want to do here is keep string
sorted according to their length, from longest to shortest. The standard library doesn't have something ready made for us but we can follow the example of greater<>
. It would look like this:
template<class T = void>
struct longer;
template<>
struct longer<void>
{
template< class T, class U>
constexpr auto operator()(T&& lhs, U&& rhs) const ->decltype(std::forward<T>(lhs) > std::forward<U>(rhs))
{
return lhs.length() > rhs.length();
}
};
Again, I don't expect you to grasp this fully. Just keep it way in the back of your head and revisit this post in a year...
Ok, so I have my custom-made comparator. I can declare my container that will hold the palindromes. As I add string
s to this container, they will automatically be kept in sorted order by length.
But wait, here's an important detail, it's possible that I may have more than one palindrome of the same length! No sweat, use multiset
. It does the exact same thing as set
but allows multiple values where the comparison will say they are equal, i.e. of the same length in our case.
int main()
{
multiset<string, longer<>> palindromes;
Good. Now, I have to read in string
s from the user, decide if they are palindromes and save them in my special container. The following lines of code are quite compressed. It's a single statement that does all what I just described.
copy_if(istream_iterator<string>(cin), istream_iterator<string>(), inserter(palindromes, palindromes.end()), [](string const& word)
{
return equal(word.begin(), word.begin() + word.size() / 2, word.rbegin());
});
Let's break it down:
copy_if(istream_iterator<string>(cin), istream_iterator<string>(),
copy_if
is a function from the standard library that takes a range of elements, applies a predicate
on every element and if the condition is satisfied, copies the element to some output. In C++, the word range
can take many strange shapes. My range here is the standard input! That's a range? Well, you can present it as such. This code will prompt the user to input some strings. Every time the user presses Enter
, that's a new string in the range. The end of the range is when the user uses the OS-specific end-of-input command, e.g. Ctrl-D in bash or Ctrl-Z in DOS.
inserter(palindromes, palindromes.begin()),
That's the output. That's where elements that satisfy the predicate will but copied. The inserter
function is how I tell copy_if
to call the member function .insert()
on my multiset
. Why do I specify .begin()
? That's make little sense. The multiset will keep the elements sorted according to the rules I gave it. It should not keep adding the elements at the beginning!
Well, that inserter
function is a very generic one. It works for all kinds of container. With a vector
, it would do as I wrote it. But with a set
or multiset
, that .begin()
is just a hint and will be discarded.
On to the predicate:
[](string const& word)
{
return equal(word.begin(), word.begin() + word.size() / 2, word.rbegin());
});
Well, I have no shame to say that I just copy-pasted @MORTAL's code for that part. :) That's a lambda
, an in-place function that you don't need to write somewhere, you just write exactly where you need it. Apart from the funny syntax, the body is the same as the more mundane but equivalent function.
So now we have all palindromes saved and ordered by length order. Let's print them out:
copy(palindromes.begin(), palindromes.end(), ostream_iterator<string>(cout, "\n"));
Another call to the copy
family. This is a plain copy and it will copy all elements where I tell it to. In this case, all elements will printed to standard output, with a newline in between. They will show up from the longest to the shortest.
And we're done.
return 0;
}
I will add one last thing. The fact that I use a set
(or multiset
) does mean that this code is doing a bit more work than what you needed given your problem statement. That is, you did not necessarily wanted to keep all the palindromes sorted, you just wanted to know which one was the longest. But I offered this solution as an example to leverage stuff from the standard library that can do a lot of work for you transparently.