Hacker Rank Challenge : Find count of substrings which are special palindrome
I am getting timeout for test cases having very large input.
-= Challenge Link =-
-= Test case example =-
Code is working fine as can be checked on ideone. (Note: Here fout
has been replaced with cout
.)
Problem Statement :
A string is said to be a special palindromic string if either of two
conditions is met:
- All of the characters are the same, e.g. aaa.
- All characters except the middle one are the same, e.g. aadaa.
A special palindromic
substring is any substring of a string which meets one of those
criteria. Given a string, determine how many special palindromic
substrings can be formed from it.
For example, given the string s = mnonopoo , we have the following special
palindromic substrings: {m, n, o, n, o, p, o, o, non, ono, opo, oo}.
Function Description
Complete the substrCount function in the editor below. It should
return an integer representing the number of special palindromic
substrings that can be formed from the given string.
substrCount has the following parameter(s):
- n: an integer, the length of string s
- s: a string
Input Format
The first line contains an integer, n , the length of s. The second line
contains the string .
Constraints
1 ≤ n ≤ 10^6
Each character of the string is a lowercase alphabet, ascii[a-z].
Output Format
Print a single line containing the count of total special palindromic
substrings.
Sample Input 0
5
asasd
Sample Output 0
7
Explanation 0
The special palindromic substrings of s = asasd are {a, s, a, s, d, asa, sas}.
Sample Input 1
7
abcbaba
Sample Output 1
10
Explanation 1
The special palindromic substrings of s = abcbaba are {a, b, c, b, a, b, a, bcb, bab, aba}.
Sample Input 2
4
aaaa
Sample Output 2
10
Explanation 2
The special palindromic substrings of s = aaaa are {a, a, a, a, aa, aa, aa, aaa, aaa, aaaa}.
Code:
#include <bits/stdc++.h>
using namespace std;
// Complete the substrCount function below.
long substrCount(int n, string s) {
long int length_sub = 2;
long int count = n;
while(length_sub <= n){
for(long int i = 0; i <= n - length_sub ; i++){
string sub = s.substr(i, length_sub);
//cout << sub << " ";
string rev_sub(sub);
reverse(rev_sub.begin(), rev_sub.end());
//cout << rev_sub;;
char c = sub[0];
int flag = 0;
for(long int j = 0; j < sub.length() / 2; j++){
if(sub[j] != c || rev_sub[j] != c){
flag = 1;
break;
}
}
if(flag == 0){
//cout << " - Specialn";
count++;
}
// else{
// cout << "n";
// }
}
length_sub++;
}
return count;
}
int main()
{
ofstream fout(getenv("OUTPUT_PATH"));
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), 'n');
string s;
getline(cin, s);
long result = substrCount(n, s);
fout << result << "n";
fout.close();
return 0;
}
c++ algorithm programming-challenge time-limit-exceeded c++14
|
show 1 more comment
I am getting timeout for test cases having very large input.
-= Challenge Link =-
-= Test case example =-
Code is working fine as can be checked on ideone. (Note: Here fout
has been replaced with cout
.)
Problem Statement :
A string is said to be a special palindromic string if either of two
conditions is met:
- All of the characters are the same, e.g. aaa.
- All characters except the middle one are the same, e.g. aadaa.
A special palindromic
substring is any substring of a string which meets one of those
criteria. Given a string, determine how many special palindromic
substrings can be formed from it.
For example, given the string s = mnonopoo , we have the following special
palindromic substrings: {m, n, o, n, o, p, o, o, non, ono, opo, oo}.
Function Description
Complete the substrCount function in the editor below. It should
return an integer representing the number of special palindromic
substrings that can be formed from the given string.
substrCount has the following parameter(s):
- n: an integer, the length of string s
- s: a string
Input Format
The first line contains an integer, n , the length of s. The second line
contains the string .
Constraints
1 ≤ n ≤ 10^6
Each character of the string is a lowercase alphabet, ascii[a-z].
Output Format
Print a single line containing the count of total special palindromic
substrings.
Sample Input 0
5
asasd
Sample Output 0
7
Explanation 0
The special palindromic substrings of s = asasd are {a, s, a, s, d, asa, sas}.
Sample Input 1
7
abcbaba
Sample Output 1
10
Explanation 1
The special palindromic substrings of s = abcbaba are {a, b, c, b, a, b, a, bcb, bab, aba}.
Sample Input 2
4
aaaa
Sample Output 2
10
Explanation 2
The special palindromic substrings of s = aaaa are {a, a, a, a, aa, aa, aa, aaa, aaa, aaaa}.
Code:
#include <bits/stdc++.h>
using namespace std;
// Complete the substrCount function below.
long substrCount(int n, string s) {
long int length_sub = 2;
long int count = n;
while(length_sub <= n){
for(long int i = 0; i <= n - length_sub ; i++){
string sub = s.substr(i, length_sub);
//cout << sub << " ";
string rev_sub(sub);
reverse(rev_sub.begin(), rev_sub.end());
//cout << rev_sub;;
char c = sub[0];
int flag = 0;
for(long int j = 0; j < sub.length() / 2; j++){
if(sub[j] != c || rev_sub[j] != c){
flag = 1;
break;
}
}
if(flag == 0){
//cout << " - Specialn";
count++;
}
// else{
// cout << "n";
// }
}
length_sub++;
}
return count;
}
int main()
{
ofstream fout(getenv("OUTPUT_PATH"));
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), 'n');
string s;
getline(cin, s);
long result = substrCount(n, s);
fout << result << "n";
fout.close();
return 0;
}
c++ algorithm programming-challenge time-limit-exceeded c++14
1
Not sure it belongs here... codereview isn't about improving algorithms as far as I know. That said, a hint: don't create an endless stream of substrings when you can use iterators instead
– papagaga
Aug 30 at 15:26
5
@papagaga Improving algorithms is absolutely part of Code Review. That's what many of the algorithm and time-limit-exceeded questions are about.
– 200_success
Aug 30 at 18:03
1
@papagaga Algorithms can always be improved—often immensely. As long as the code posted meets the requirements of the site, it's on-topic. All parts are considered reviewable, even if its a question that is masquerading as a code review for some other nefarious purpose.
– Snowhawk
Aug 30 at 18:04
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mast
Aug 31 at 5:50
1
@Phoenix Which was pointed out in an answer. So if you remove them, that part of the answer makes no sense. That's answer invalidation.
– Mast
Aug 31 at 7:08
|
show 1 more comment
I am getting timeout for test cases having very large input.
-= Challenge Link =-
-= Test case example =-
Code is working fine as can be checked on ideone. (Note: Here fout
has been replaced with cout
.)
Problem Statement :
A string is said to be a special palindromic string if either of two
conditions is met:
- All of the characters are the same, e.g. aaa.
- All characters except the middle one are the same, e.g. aadaa.
A special palindromic
substring is any substring of a string which meets one of those
criteria. Given a string, determine how many special palindromic
substrings can be formed from it.
For example, given the string s = mnonopoo , we have the following special
palindromic substrings: {m, n, o, n, o, p, o, o, non, ono, opo, oo}.
Function Description
Complete the substrCount function in the editor below. It should
return an integer representing the number of special palindromic
substrings that can be formed from the given string.
substrCount has the following parameter(s):
- n: an integer, the length of string s
- s: a string
Input Format
The first line contains an integer, n , the length of s. The second line
contains the string .
Constraints
1 ≤ n ≤ 10^6
Each character of the string is a lowercase alphabet, ascii[a-z].
Output Format
Print a single line containing the count of total special palindromic
substrings.
Sample Input 0
5
asasd
Sample Output 0
7
Explanation 0
The special palindromic substrings of s = asasd are {a, s, a, s, d, asa, sas}.
Sample Input 1
7
abcbaba
Sample Output 1
10
Explanation 1
The special palindromic substrings of s = abcbaba are {a, b, c, b, a, b, a, bcb, bab, aba}.
Sample Input 2
4
aaaa
Sample Output 2
10
Explanation 2
The special palindromic substrings of s = aaaa are {a, a, a, a, aa, aa, aa, aaa, aaa, aaaa}.
Code:
#include <bits/stdc++.h>
using namespace std;
// Complete the substrCount function below.
long substrCount(int n, string s) {
long int length_sub = 2;
long int count = n;
while(length_sub <= n){
for(long int i = 0; i <= n - length_sub ; i++){
string sub = s.substr(i, length_sub);
//cout << sub << " ";
string rev_sub(sub);
reverse(rev_sub.begin(), rev_sub.end());
//cout << rev_sub;;
char c = sub[0];
int flag = 0;
for(long int j = 0; j < sub.length() / 2; j++){
if(sub[j] != c || rev_sub[j] != c){
flag = 1;
break;
}
}
if(flag == 0){
//cout << " - Specialn";
count++;
}
// else{
// cout << "n";
// }
}
length_sub++;
}
return count;
}
int main()
{
ofstream fout(getenv("OUTPUT_PATH"));
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), 'n');
string s;
getline(cin, s);
long result = substrCount(n, s);
fout << result << "n";
fout.close();
return 0;
}
c++ algorithm programming-challenge time-limit-exceeded c++14
I am getting timeout for test cases having very large input.
-= Challenge Link =-
-= Test case example =-
Code is working fine as can be checked on ideone. (Note: Here fout
has been replaced with cout
.)
Problem Statement :
A string is said to be a special palindromic string if either of two
conditions is met:
- All of the characters are the same, e.g. aaa.
- All characters except the middle one are the same, e.g. aadaa.
A special palindromic
substring is any substring of a string which meets one of those
criteria. Given a string, determine how many special palindromic
substrings can be formed from it.
For example, given the string s = mnonopoo , we have the following special
palindromic substrings: {m, n, o, n, o, p, o, o, non, ono, opo, oo}.
Function Description
Complete the substrCount function in the editor below. It should
return an integer representing the number of special palindromic
substrings that can be formed from the given string.
substrCount has the following parameter(s):
- n: an integer, the length of string s
- s: a string
Input Format
The first line contains an integer, n , the length of s. The second line
contains the string .
Constraints
1 ≤ n ≤ 10^6
Each character of the string is a lowercase alphabet, ascii[a-z].
Output Format
Print a single line containing the count of total special palindromic
substrings.
Sample Input 0
5
asasd
Sample Output 0
7
Explanation 0
The special palindromic substrings of s = asasd are {a, s, a, s, d, asa, sas}.
Sample Input 1
7
abcbaba
Sample Output 1
10
Explanation 1
The special palindromic substrings of s = abcbaba are {a, b, c, b, a, b, a, bcb, bab, aba}.
Sample Input 2
4
aaaa
Sample Output 2
10
Explanation 2
The special palindromic substrings of s = aaaa are {a, a, a, a, aa, aa, aa, aaa, aaa, aaaa}.
Code:
#include <bits/stdc++.h>
using namespace std;
// Complete the substrCount function below.
long substrCount(int n, string s) {
long int length_sub = 2;
long int count = n;
while(length_sub <= n){
for(long int i = 0; i <= n - length_sub ; i++){
string sub = s.substr(i, length_sub);
//cout << sub << " ";
string rev_sub(sub);
reverse(rev_sub.begin(), rev_sub.end());
//cout << rev_sub;;
char c = sub[0];
int flag = 0;
for(long int j = 0; j < sub.length() / 2; j++){
if(sub[j] != c || rev_sub[j] != c){
flag = 1;
break;
}
}
if(flag == 0){
//cout << " - Specialn";
count++;
}
// else{
// cout << "n";
// }
}
length_sub++;
}
return count;
}
int main()
{
ofstream fout(getenv("OUTPUT_PATH"));
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), 'n');
string s;
getline(cin, s);
long result = substrCount(n, s);
fout << result << "n";
fout.close();
return 0;
}
c++ algorithm programming-challenge time-limit-exceeded c++14
c++ algorithm programming-challenge time-limit-exceeded c++14
edited Aug 31 at 5:50
Mast
7,43863686
7,43863686
asked Aug 30 at 15:04
Phoenix
1216
1216
1
Not sure it belongs here... codereview isn't about improving algorithms as far as I know. That said, a hint: don't create an endless stream of substrings when you can use iterators instead
– papagaga
Aug 30 at 15:26
5
@papagaga Improving algorithms is absolutely part of Code Review. That's what many of the algorithm and time-limit-exceeded questions are about.
– 200_success
Aug 30 at 18:03
1
@papagaga Algorithms can always be improved—often immensely. As long as the code posted meets the requirements of the site, it's on-topic. All parts are considered reviewable, even if its a question that is masquerading as a code review for some other nefarious purpose.
– Snowhawk
Aug 30 at 18:04
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mast
Aug 31 at 5:50
1
@Phoenix Which was pointed out in an answer. So if you remove them, that part of the answer makes no sense. That's answer invalidation.
– Mast
Aug 31 at 7:08
|
show 1 more comment
1
Not sure it belongs here... codereview isn't about improving algorithms as far as I know. That said, a hint: don't create an endless stream of substrings when you can use iterators instead
– papagaga
Aug 30 at 15:26
5
@papagaga Improving algorithms is absolutely part of Code Review. That's what many of the algorithm and time-limit-exceeded questions are about.
– 200_success
Aug 30 at 18:03
1
@papagaga Algorithms can always be improved—often immensely. As long as the code posted meets the requirements of the site, it's on-topic. All parts are considered reviewable, even if its a question that is masquerading as a code review for some other nefarious purpose.
– Snowhawk
Aug 30 at 18:04
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mast
Aug 31 at 5:50
1
@Phoenix Which was pointed out in an answer. So if you remove them, that part of the answer makes no sense. That's answer invalidation.
– Mast
Aug 31 at 7:08
1
1
Not sure it belongs here... codereview isn't about improving algorithms as far as I know. That said, a hint: don't create an endless stream of substrings when you can use iterators instead
– papagaga
Aug 30 at 15:26
Not sure it belongs here... codereview isn't about improving algorithms as far as I know. That said, a hint: don't create an endless stream of substrings when you can use iterators instead
– papagaga
Aug 30 at 15:26
5
5
@papagaga Improving algorithms is absolutely part of Code Review. That's what many of the algorithm and time-limit-exceeded questions are about.
– 200_success
Aug 30 at 18:03
@papagaga Improving algorithms is absolutely part of Code Review. That's what many of the algorithm and time-limit-exceeded questions are about.
– 200_success
Aug 30 at 18:03
1
1
@papagaga Algorithms can always be improved—often immensely. As long as the code posted meets the requirements of the site, it's on-topic. All parts are considered reviewable, even if its a question that is masquerading as a code review for some other nefarious purpose.
– Snowhawk
Aug 30 at 18:04
@papagaga Algorithms can always be improved—often immensely. As long as the code posted meets the requirements of the site, it's on-topic. All parts are considered reviewable, even if its a question that is masquerading as a code review for some other nefarious purpose.
– Snowhawk
Aug 30 at 18:04
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mast
Aug 31 at 5:50
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mast
Aug 31 at 5:50
1
1
@Phoenix Which was pointed out in an answer. So if you remove them, that part of the answer makes no sense. That's answer invalidation.
– Mast
Aug 31 at 7:08
@Phoenix Which was pointed out in an answer. So if you remove them, that part of the answer makes no sense. That's answer invalidation.
– Mast
Aug 31 at 7:08
|
show 1 more comment
2 Answers
2
active
oldest
votes
This
#include <bits/stdc++.h>
using namespace std;
is given by the submission template from HackerRank (so it is not your fault),
but note that both lines are generally considered as bad practice.
See for example
- Why should I not
#include <bits/stdc++.h>
? - Why is “using namespace std” considered bad practice?
on Stack Overflow.
With respect to your
long substrCount(int n, string s)
function:
- The outer
while
loop could be made afor
loop as well – why should it
be different from the innerfor(long int ...)
loop? - The proper data type for
string
lengths and indices isstring::size_type
akasize_t
.
One step to increase the efficiency would be to avoid the creation (and reversal)
of the additional strings sub
and rev_sub
. All tests can be done directly on
the original string s
. As an example,
if (sub[j] != c || rev_sub[j] != c)
is equivalent to
if (s[i + j] != c || s[i + length_sub - 1 - j] != c)
Your method checks all $ n (n-1)/2 $ substrings of length 2 or more if
it is a “special palindromic string.”. The following approach seems more
efficient to me:
The number of substrings with all identical characters (e.g. "aaa")
can be determined with a single traversal of the string. All sequences
of $ k $ contiguous identical characters contain $ k(k+1)/2 $
substrings with identical characters.To determine the number of substrings where characters except the middle one
are the same (e.g. "aadaa") traverse the string, and check for each character
(e.g. "d") how many identical characters exist to the left and to the right
of this character.
Example: The string "mnonopoo" has the following sequences of contiguous
identical characters
m n o n o p oo
which gives a total
1+1+1+1+1+1+3 = 9
substrings with all identical characters. The number of substrings where
all characters except the middle one are the same around each character are
m n o n o p o o
0+0+1+1+0+1+0+0 = 3
add a comment |
First, the things which are bad about the form:
#include <bits/stdc++.h>
is non-standard and probably far too much.using namespace std;
is evil becausestd
is not designed to be imported wholesale, making conflicts and silent changes, now and in future, likely.long substrCount(int n, string s)
is also a bad idea.n
duplicatess.size()
but with the wrong type (it should bestring::size_type
).The code assumes that input won't fail. That's generally wrong.
return 0;
is implicit formain()
.
Now, about your code:
All your comments are just a distraction, as they contain cryptic code-fragments. Clean them up.
If you want to create a reverse of a range, just initialize the copy with reverse-iterators instead of copying and then reversing.
rbegin()
andrend()
are your friends.
If you want to know whether a range is all copies of the same element except the middle, take a look at
std::count()
:
bool my_palindrome = range[range.size() / 2] != range[0]
&& std::count(range.begin(), range.end(), range[0]) == range.size() - 1;
Making copies, reversing, and then comparing is just superfluous additional work.
Fixing all those small things might lead to a significant speed-up, but it doesn't improve the order of your algorithm which is $O(n^2)$.
For that, move to a different algorithm:
- Start with zero.
- Find runs of identical characters.
- Add the number of substrings for the run, which is $k * (k + 1) / 2$.
- If the run has length one, and the bordering runs consist of identical characters, add the length of the smaller one.
That's $O(n)$, even single-pass.
Improved version of your function (using C++17 std::string_view
as the parameter to avoid any copies, even in the caller, whether he has a std::string
or not):
long substrCount(std::string_view s) noexcept {
char c[3] = {};
long n[3] = {};
long r = 0;
for (auto curr = begin(s), last = end(s), pos = begin(s); curr != last; curr = pos) {
pos = std::find_if(curr + 1, last, [=](char c){ return c != *curr; });
std::tie(n[2], n[1], n[0]) = std::make_tuple(n[1], n[0], pos - curr);
std::tie(c[2], c[1], c[0]) = std::make_tuple(c[1], c[0], *curr);
r += *n * (*n + 1) / 2;
if (n[1] == 1 && c[0] == c[2])
r += std::min(n[0], n[2]);
}
return r;
}
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
1
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunatelyboost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.
– Deduplicator
Aug 31 at 14:25
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f202820%2fhacker-rank-challenge-find-count-of-substrings-which-are-special-palindrome%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
This
#include <bits/stdc++.h>
using namespace std;
is given by the submission template from HackerRank (so it is not your fault),
but note that both lines are generally considered as bad practice.
See for example
- Why should I not
#include <bits/stdc++.h>
? - Why is “using namespace std” considered bad practice?
on Stack Overflow.
With respect to your
long substrCount(int n, string s)
function:
- The outer
while
loop could be made afor
loop as well – why should it
be different from the innerfor(long int ...)
loop? - The proper data type for
string
lengths and indices isstring::size_type
akasize_t
.
One step to increase the efficiency would be to avoid the creation (and reversal)
of the additional strings sub
and rev_sub
. All tests can be done directly on
the original string s
. As an example,
if (sub[j] != c || rev_sub[j] != c)
is equivalent to
if (s[i + j] != c || s[i + length_sub - 1 - j] != c)
Your method checks all $ n (n-1)/2 $ substrings of length 2 or more if
it is a “special palindromic string.”. The following approach seems more
efficient to me:
The number of substrings with all identical characters (e.g. "aaa")
can be determined with a single traversal of the string. All sequences
of $ k $ contiguous identical characters contain $ k(k+1)/2 $
substrings with identical characters.To determine the number of substrings where characters except the middle one
are the same (e.g. "aadaa") traverse the string, and check for each character
(e.g. "d") how many identical characters exist to the left and to the right
of this character.
Example: The string "mnonopoo" has the following sequences of contiguous
identical characters
m n o n o p oo
which gives a total
1+1+1+1+1+1+3 = 9
substrings with all identical characters. The number of substrings where
all characters except the middle one are the same around each character are
m n o n o p o o
0+0+1+1+0+1+0+0 = 3
add a comment |
This
#include <bits/stdc++.h>
using namespace std;
is given by the submission template from HackerRank (so it is not your fault),
but note that both lines are generally considered as bad practice.
See for example
- Why should I not
#include <bits/stdc++.h>
? - Why is “using namespace std” considered bad practice?
on Stack Overflow.
With respect to your
long substrCount(int n, string s)
function:
- The outer
while
loop could be made afor
loop as well – why should it
be different from the innerfor(long int ...)
loop? - The proper data type for
string
lengths and indices isstring::size_type
akasize_t
.
One step to increase the efficiency would be to avoid the creation (and reversal)
of the additional strings sub
and rev_sub
. All tests can be done directly on
the original string s
. As an example,
if (sub[j] != c || rev_sub[j] != c)
is equivalent to
if (s[i + j] != c || s[i + length_sub - 1 - j] != c)
Your method checks all $ n (n-1)/2 $ substrings of length 2 or more if
it is a “special palindromic string.”. The following approach seems more
efficient to me:
The number of substrings with all identical characters (e.g. "aaa")
can be determined with a single traversal of the string. All sequences
of $ k $ contiguous identical characters contain $ k(k+1)/2 $
substrings with identical characters.To determine the number of substrings where characters except the middle one
are the same (e.g. "aadaa") traverse the string, and check for each character
(e.g. "d") how many identical characters exist to the left and to the right
of this character.
Example: The string "mnonopoo" has the following sequences of contiguous
identical characters
m n o n o p oo
which gives a total
1+1+1+1+1+1+3 = 9
substrings with all identical characters. The number of substrings where
all characters except the middle one are the same around each character are
m n o n o p o o
0+0+1+1+0+1+0+0 = 3
add a comment |
This
#include <bits/stdc++.h>
using namespace std;
is given by the submission template from HackerRank (so it is not your fault),
but note that both lines are generally considered as bad practice.
See for example
- Why should I not
#include <bits/stdc++.h>
? - Why is “using namespace std” considered bad practice?
on Stack Overflow.
With respect to your
long substrCount(int n, string s)
function:
- The outer
while
loop could be made afor
loop as well – why should it
be different from the innerfor(long int ...)
loop? - The proper data type for
string
lengths and indices isstring::size_type
akasize_t
.
One step to increase the efficiency would be to avoid the creation (and reversal)
of the additional strings sub
and rev_sub
. All tests can be done directly on
the original string s
. As an example,
if (sub[j] != c || rev_sub[j] != c)
is equivalent to
if (s[i + j] != c || s[i + length_sub - 1 - j] != c)
Your method checks all $ n (n-1)/2 $ substrings of length 2 or more if
it is a “special palindromic string.”. The following approach seems more
efficient to me:
The number of substrings with all identical characters (e.g. "aaa")
can be determined with a single traversal of the string. All sequences
of $ k $ contiguous identical characters contain $ k(k+1)/2 $
substrings with identical characters.To determine the number of substrings where characters except the middle one
are the same (e.g. "aadaa") traverse the string, and check for each character
(e.g. "d") how many identical characters exist to the left and to the right
of this character.
Example: The string "mnonopoo" has the following sequences of contiguous
identical characters
m n o n o p oo
which gives a total
1+1+1+1+1+1+3 = 9
substrings with all identical characters. The number of substrings where
all characters except the middle one are the same around each character are
m n o n o p o o
0+0+1+1+0+1+0+0 = 3
This
#include <bits/stdc++.h>
using namespace std;
is given by the submission template from HackerRank (so it is not your fault),
but note that both lines are generally considered as bad practice.
See for example
- Why should I not
#include <bits/stdc++.h>
? - Why is “using namespace std” considered bad practice?
on Stack Overflow.
With respect to your
long substrCount(int n, string s)
function:
- The outer
while
loop could be made afor
loop as well – why should it
be different from the innerfor(long int ...)
loop? - The proper data type for
string
lengths and indices isstring::size_type
akasize_t
.
One step to increase the efficiency would be to avoid the creation (and reversal)
of the additional strings sub
and rev_sub
. All tests can be done directly on
the original string s
. As an example,
if (sub[j] != c || rev_sub[j] != c)
is equivalent to
if (s[i + j] != c || s[i + length_sub - 1 - j] != c)
Your method checks all $ n (n-1)/2 $ substrings of length 2 or more if
it is a “special palindromic string.”. The following approach seems more
efficient to me:
The number of substrings with all identical characters (e.g. "aaa")
can be determined with a single traversal of the string. All sequences
of $ k $ contiguous identical characters contain $ k(k+1)/2 $
substrings with identical characters.To determine the number of substrings where characters except the middle one
are the same (e.g. "aadaa") traverse the string, and check for each character
(e.g. "d") how many identical characters exist to the left and to the right
of this character.
Example: The string "mnonopoo" has the following sequences of contiguous
identical characters
m n o n o p oo
which gives a total
1+1+1+1+1+1+3 = 9
substrings with all identical characters. The number of substrings where
all characters except the middle one are the same around each character are
m n o n o p o o
0+0+1+1+0+1+0+0 = 3
edited Aug 31 at 11:49
answered Aug 30 at 19:14
Martin R
15.5k12264
15.5k12264
add a comment |
add a comment |
First, the things which are bad about the form:
#include <bits/stdc++.h>
is non-standard and probably far too much.using namespace std;
is evil becausestd
is not designed to be imported wholesale, making conflicts and silent changes, now and in future, likely.long substrCount(int n, string s)
is also a bad idea.n
duplicatess.size()
but with the wrong type (it should bestring::size_type
).The code assumes that input won't fail. That's generally wrong.
return 0;
is implicit formain()
.
Now, about your code:
All your comments are just a distraction, as they contain cryptic code-fragments. Clean them up.
If you want to create a reverse of a range, just initialize the copy with reverse-iterators instead of copying and then reversing.
rbegin()
andrend()
are your friends.
If you want to know whether a range is all copies of the same element except the middle, take a look at
std::count()
:
bool my_palindrome = range[range.size() / 2] != range[0]
&& std::count(range.begin(), range.end(), range[0]) == range.size() - 1;
Making copies, reversing, and then comparing is just superfluous additional work.
Fixing all those small things might lead to a significant speed-up, but it doesn't improve the order of your algorithm which is $O(n^2)$.
For that, move to a different algorithm:
- Start with zero.
- Find runs of identical characters.
- Add the number of substrings for the run, which is $k * (k + 1) / 2$.
- If the run has length one, and the bordering runs consist of identical characters, add the length of the smaller one.
That's $O(n)$, even single-pass.
Improved version of your function (using C++17 std::string_view
as the parameter to avoid any copies, even in the caller, whether he has a std::string
or not):
long substrCount(std::string_view s) noexcept {
char c[3] = {};
long n[3] = {};
long r = 0;
for (auto curr = begin(s), last = end(s), pos = begin(s); curr != last; curr = pos) {
pos = std::find_if(curr + 1, last, [=](char c){ return c != *curr; });
std::tie(n[2], n[1], n[0]) = std::make_tuple(n[1], n[0], pos - curr);
std::tie(c[2], c[1], c[0]) = std::make_tuple(c[1], c[0], *curr);
r += *n * (*n + 1) / 2;
if (n[1] == 1 && c[0] == c[2])
r += std::min(n[0], n[2]);
}
return r;
}
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
1
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunatelyboost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.
– Deduplicator
Aug 31 at 14:25
add a comment |
First, the things which are bad about the form:
#include <bits/stdc++.h>
is non-standard and probably far too much.using namespace std;
is evil becausestd
is not designed to be imported wholesale, making conflicts and silent changes, now and in future, likely.long substrCount(int n, string s)
is also a bad idea.n
duplicatess.size()
but with the wrong type (it should bestring::size_type
).The code assumes that input won't fail. That's generally wrong.
return 0;
is implicit formain()
.
Now, about your code:
All your comments are just a distraction, as they contain cryptic code-fragments. Clean them up.
If you want to create a reverse of a range, just initialize the copy with reverse-iterators instead of copying and then reversing.
rbegin()
andrend()
are your friends.
If you want to know whether a range is all copies of the same element except the middle, take a look at
std::count()
:
bool my_palindrome = range[range.size() / 2] != range[0]
&& std::count(range.begin(), range.end(), range[0]) == range.size() - 1;
Making copies, reversing, and then comparing is just superfluous additional work.
Fixing all those small things might lead to a significant speed-up, but it doesn't improve the order of your algorithm which is $O(n^2)$.
For that, move to a different algorithm:
- Start with zero.
- Find runs of identical characters.
- Add the number of substrings for the run, which is $k * (k + 1) / 2$.
- If the run has length one, and the bordering runs consist of identical characters, add the length of the smaller one.
That's $O(n)$, even single-pass.
Improved version of your function (using C++17 std::string_view
as the parameter to avoid any copies, even in the caller, whether he has a std::string
or not):
long substrCount(std::string_view s) noexcept {
char c[3] = {};
long n[3] = {};
long r = 0;
for (auto curr = begin(s), last = end(s), pos = begin(s); curr != last; curr = pos) {
pos = std::find_if(curr + 1, last, [=](char c){ return c != *curr; });
std::tie(n[2], n[1], n[0]) = std::make_tuple(n[1], n[0], pos - curr);
std::tie(c[2], c[1], c[0]) = std::make_tuple(c[1], c[0], *curr);
r += *n * (*n + 1) / 2;
if (n[1] == 1 && c[0] == c[2])
r += std::min(n[0], n[2]);
}
return r;
}
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
1
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunatelyboost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.
– Deduplicator
Aug 31 at 14:25
add a comment |
First, the things which are bad about the form:
#include <bits/stdc++.h>
is non-standard and probably far too much.using namespace std;
is evil becausestd
is not designed to be imported wholesale, making conflicts and silent changes, now and in future, likely.long substrCount(int n, string s)
is also a bad idea.n
duplicatess.size()
but with the wrong type (it should bestring::size_type
).The code assumes that input won't fail. That's generally wrong.
return 0;
is implicit formain()
.
Now, about your code:
All your comments are just a distraction, as they contain cryptic code-fragments. Clean them up.
If you want to create a reverse of a range, just initialize the copy with reverse-iterators instead of copying and then reversing.
rbegin()
andrend()
are your friends.
If you want to know whether a range is all copies of the same element except the middle, take a look at
std::count()
:
bool my_palindrome = range[range.size() / 2] != range[0]
&& std::count(range.begin(), range.end(), range[0]) == range.size() - 1;
Making copies, reversing, and then comparing is just superfluous additional work.
Fixing all those small things might lead to a significant speed-up, but it doesn't improve the order of your algorithm which is $O(n^2)$.
For that, move to a different algorithm:
- Start with zero.
- Find runs of identical characters.
- Add the number of substrings for the run, which is $k * (k + 1) / 2$.
- If the run has length one, and the bordering runs consist of identical characters, add the length of the smaller one.
That's $O(n)$, even single-pass.
Improved version of your function (using C++17 std::string_view
as the parameter to avoid any copies, even in the caller, whether he has a std::string
or not):
long substrCount(std::string_view s) noexcept {
char c[3] = {};
long n[3] = {};
long r = 0;
for (auto curr = begin(s), last = end(s), pos = begin(s); curr != last; curr = pos) {
pos = std::find_if(curr + 1, last, [=](char c){ return c != *curr; });
std::tie(n[2], n[1], n[0]) = std::make_tuple(n[1], n[0], pos - curr);
std::tie(c[2], c[1], c[0]) = std::make_tuple(c[1], c[0], *curr);
r += *n * (*n + 1) / 2;
if (n[1] == 1 && c[0] == c[2])
r += std::min(n[0], n[2]);
}
return r;
}
First, the things which are bad about the form:
#include <bits/stdc++.h>
is non-standard and probably far too much.using namespace std;
is evil becausestd
is not designed to be imported wholesale, making conflicts and silent changes, now and in future, likely.long substrCount(int n, string s)
is also a bad idea.n
duplicatess.size()
but with the wrong type (it should bestring::size_type
).The code assumes that input won't fail. That's generally wrong.
return 0;
is implicit formain()
.
Now, about your code:
All your comments are just a distraction, as they contain cryptic code-fragments. Clean them up.
If you want to create a reverse of a range, just initialize the copy with reverse-iterators instead of copying and then reversing.
rbegin()
andrend()
are your friends.
If you want to know whether a range is all copies of the same element except the middle, take a look at
std::count()
:
bool my_palindrome = range[range.size() / 2] != range[0]
&& std::count(range.begin(), range.end(), range[0]) == range.size() - 1;
Making copies, reversing, and then comparing is just superfluous additional work.
Fixing all those small things might lead to a significant speed-up, but it doesn't improve the order of your algorithm which is $O(n^2)$.
For that, move to a different algorithm:
- Start with zero.
- Find runs of identical characters.
- Add the number of substrings for the run, which is $k * (k + 1) / 2$.
- If the run has length one, and the bordering runs consist of identical characters, add the length of the smaller one.
That's $O(n)$, even single-pass.
Improved version of your function (using C++17 std::string_view
as the parameter to avoid any copies, even in the caller, whether he has a std::string
or not):
long substrCount(std::string_view s) noexcept {
char c[3] = {};
long n[3] = {};
long r = 0;
for (auto curr = begin(s), last = end(s), pos = begin(s); curr != last; curr = pos) {
pos = std::find_if(curr + 1, last, [=](char c){ return c != *curr; });
std::tie(n[2], n[1], n[0]) = std::make_tuple(n[1], n[0], pos - curr);
std::tie(c[2], c[1], c[0]) = std::make_tuple(c[1], c[0], *curr);
r += *n * (*n + 1) / 2;
if (n[1] == 1 && c[0] == c[2])
r += std::min(n[0], n[2]);
}
return r;
}
edited 39 mins ago
answered Aug 30 at 22:36
Deduplicator
10.9k1849
10.9k1849
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
1
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunatelyboost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.
– Deduplicator
Aug 31 at 14:25
add a comment |
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
1
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunatelyboost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.
– Deduplicator
Aug 31 at 14:25
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
@Deduplicator why did you change the code ?
– Phoenix
Aug 31 at 13:17
1
1
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunately
boost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.– Deduplicator
Aug 31 at 14:25
The changed code dispenses with the lambda I needed earlier to avoid duplication, and it's shorter by 7 of 25 lines. Unfortunately
boost::find_not
never made it into the standard, or it would be even better. And I didn't want to include part of boost here just for that.– Deduplicator
Aug 31 at 14:25
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f202820%2fhacker-rank-challenge-find-count-of-substrings-which-are-special-palindrome%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Not sure it belongs here... codereview isn't about improving algorithms as far as I know. That said, a hint: don't create an endless stream of substrings when you can use iterators instead
– papagaga
Aug 30 at 15:26
5
@papagaga Improving algorithms is absolutely part of Code Review. That's what many of the algorithm and time-limit-exceeded questions are about.
– 200_success
Aug 30 at 18:03
1
@papagaga Algorithms can always be improved—often immensely. As long as the code posted meets the requirements of the site, it's on-topic. All parts are considered reviewable, even if its a question that is masquerading as a code review for some other nefarious purpose.
– Snowhawk
Aug 30 at 18:04
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
– Mast
Aug 31 at 5:50
1
@Phoenix Which was pointed out in an answer. So if you remove them, that part of the answer makes no sense. That's answer invalidation.
– Mast
Aug 31 at 7:08