-3

I recenty started solving questions on leetcode and came accross this Questions where we had to remove all alphanumeric characters from a string and then check if the string is a palindrome or not. I tried to keep O(n) time complexity still it took 54 ms which is way too long can someone please explain. Here is the code:

class Solution {
public:
    bool isPalindrome(string s) {
        int n = 0;
        transform(s.begin(),s.end(),s.begin(),::tolower);
        while(s[n] != '\0'){
            if(isalnum(s[n]) == 0 ){
                s.erase(s.begin()+n);
            }
            else{
            n++;
            }
        }

        string check = s;
        reverse(s.begin(),s.end());

        if(s == check){
            return true;
        }
        else{
            return false;
        }

    }
};

I was wanted to know what part of code exaclty is taking time

9
  • 13
    Your algorithm is not O(n).
    – trincot
    Commented Jun 18 at 15:02
  • 4
    Do not tag this as dsa. The dsa tag is for Digital Signature Algorithm. Commented Jun 18 at 15:12
  • 6
    I think the mistake you are making is assuming that any code not written by you doesn't count but that is not true. erase by itself is an O(n) operation, and you have put that inside an O(n) loop. Therefore your code is actually O(n^2).
    – john
    Commented Jun 18 at 15:17
  • 8
    O(n) time complexity has no bearing on whether the algorithm will take 54 ms. Commented Jun 18 at 15:21
  • 9
    I recenty started solving questions on leetcode -- Which once again proves that Leetcode does not teach C++. If you had known about the algorithm functions such as std::remove_if, plus know that erase() has an overload that takes a begin and end iterator, you would not have written the loop that calls erase on every individual character you want to erase. Commented Jun 18 at 15:37

2 Answers 2

4

The complexities of all parts:

  • Transforming to lowercase: O(N)
  • Removing non-alphanumeric characters: O(N^2)
  • Reversing the string: O(N)
  • Comparing strings: O(N)

You can use two pointers with O(N) time complexity:

#include <iostream>
#include <cstdint>
#include <string>

// The following block might slightly improve the execution time;
// Can be removed;
static const auto improve_runtime = []()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    return 0;
}();

struct Solution
{
    static bool isPalindrome(const std::string &s)
    {
        for (int left = 0, right = s.size() - 1; left < right; ++left, --right)
        {
            while (left < right && !std::isalnum(s[left]))
            {
                ++left;
            }

            while (left < right && !std::isalnum(s[right]))
            {
                --right;
            }

            if (std::toupper(s[left]) != std::toupper(s[right]))
            {
                return false;
            }
        }
        return true;
    }
};

int main()
{
    std::cout << std::boolalpha << Solution().isPalindrome("A man, a plan, a canal: Panama") << "\n";
    std::cout << std::boolalpha << Solution().isPalindrome("A Santa at NASA") << "\n";
    std::cout << std::boolalpha << Solution().isPalindrome("Taco Cat") << "\n";
    std::cout << std::boolalpha << Solution().isPalindrome("Was it a cat I saw?") << "\n";
    std::cout << std::boolalpha << Solution().isPalindrome("Never odd or even") << "\n";
}

Prints

true
true
true
true
true

Comment:

  • std::isalnum returns 0 when the argument is not alpha-numeric, and non-zero when it is. While false is equal to 0, non-zero is not necessarily equal to true. So comparing with false is a bit misleading, since the type of the result is not bool; compare with 0, which is what the function actually returns. Or use the usual !isalnum(whatever). – Pete Becker

  • Why std::int_fast16_t? This makes code slower. – Marek R

4
  • 1
    #include <stdbool.h> -- Why are you giving C solutions to a question tagged as C++? Commented Jun 18 at 15:14
  • 1
    Minor point: std::isalnum returns 0 when the argument is not alpha-numeric, and non-zero when it is. While false is equal to 0, non-zero is not necessarily equal to true. So comparing with false is a bit misleading, since the type of the result is not bool; compare with 0, which is what the function actually returns. Or use the usual !isalnum(whatever). Commented Jun 18 at 16:12
  • 1
    Why std::int_fast16_t? This makes code slower. Also maximum string size is 10^5 so this type will lead to integer overflow.
    – Marek R
    Commented Jun 18 at 16:36
  • 1
    Now after your last edit code fails on empty string.
    – Marek R
    Commented Jun 18 at 16:41
1

Calling erase repeatedly is probably an inefficient way of doing this. When you use call erase on one character, the machine has to move all the following characters one space to the left. This is an O(n) operation. For example, erasing the 'E' in "HELLO" would require the computer to complete all these steps:

0 1 2 3 4
H E L L O

0 1 2 3 4
H   L L O

0 1 2 3 4
H L   L O

0 1 2 3 4
H L L   O

0 1 2 3 4
H L L O

Since you've embedded an erase call in a loop that iterates over your string, your time complexity is O(n*n)=O(n2).

You'll need to find some other way of filtering out the alphanumerics - that much is up to you.

1
  • 2
    You'll need to find a way of filtering out the alphanumerics that doesn't rely on erase -- You can still use erase efficiently by using the erase/remove_if idiom. Using that idiom, you are calling erase only once on an entire range, and not individual elements. Commented Jun 20 at 11:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.