Checking if a string is funny or not

Posted on

Problem

The idea is this: To determine whether a string is funny, create a copy of the string in reverse e.g. . Iterating through each string, compare the absolute difference in the ascii values of the characters at positions 0 and 1, 1 and 2 and so on to the end. If the list of absolute differences is the same for both strings, they are funny.

This is my Python code for the same:

def funnyString(s):
    temp = s[::-1]
    arr1,arr2 = [],[]
    for i in range(len(s)-1):
        arr1.append(abs(ord(s[i+1])-ord(s[i])))
        arr2.append(abs(ord(temp[i+1])-ord(temp[i])))
    if arr1 == arr2: return "Funny"
    else: return "Not Funny"

It works but I want to get some feedback if this is too verbose and should/can be shortened. One solution example:

acxz
bcxz

Funny
Not Funny

The same implementation in C++

string funnyString(string s) {
    string temp;
    temp = s;
    vector<int> arr1, arr2;
    reverse(temp.begin(), temp.end());
    for (int i = 0; i < s.length()-1; i++) {
        arr1.push_back(fabs(int(s[i + 1]) - int(s[i])));
        arr2.push_back(fabs(int(temp[i + 1]) - int(temp[i])));
}
    if (arr1 == arr2) return "Funny";
    else return "Not Funny";

}

Solution

  1. There’s no reason to make a reversed copy. Just index from the back.

  2. Nor to delay comparing the differences. It just means you have to save them.

  3. Following the above two points ensures the function never throws. So, in C++ mark it noexcept.

  4. Next, there’s no need to go over the string from beginning to end and from end to beginning. Just stop in the middle.

  5. In line with that, you should use C++17’s std::string_view so even the caller need not make a copy.

  6. Also, return a boolean. Leave it to the caller to Interpret it as he likes, not necessarily by choosing some English words.

  7. I have no idea why you are bringing floating-point into it with fabs(), instead of staying with integers using std::abs(). At least I’m reasonably sure the compiler will fix that inefficiency. Or will it?

Modified C++ code:

constexpr bool isFunnyString(std::string_view s) noexcept {
    std::size_t a = 0, b = size(s);
    if (b > 2)
        for (b -= 2; a < b; ++a, --b)
            if (std::abs(s[a] - s[a + 1]) != std::abs(s[b] - s[b + 1]))
                return false;
    return true;
}

  1. Separate the logic from output. funnyString shall return a boolean, and the caller would decide what to print. Also consider renaming it is_funny.

  2. There is no need to actually reverse the string. Just iterate it backwards.

  3. There is no need to create the list of abs diffs. Just compare them on the fly.

All that said, proposed solution is (in , just for fun, easily adaptable to other languages):

bool is_funny(char * s)
{
    for (int i = 1, j = strlen(s) - 1; j > 0; i++, j--) {
        if (abs(s[i] - s[i-1]) != abs(s[j] - s[j-1]) {
            return false;
        }
    }
    return true;
}

For completeness, here is a Python version along the lines of @vnp and @Deduplicator’s answers.

def funnyString(s):
    f = lambda i : abs(ord(s[i]) - ord(s[i+1]))
    return "Funny" if all(f(i)==f(-i-2) for i in range(len(s)//2)) else "Not Funny"

Instead of reversing the string s[::-1] and indexing from the start:

temp = s[::-1]
...
odd(temp[i+1])-ord(temp[i])

You can simply indexing from the back using negative indices, as shown above.

Instead of looping over all indices, recording the differences, and testing the recorded lists after the fact, we can use list comprehension and all(...) to determine if all pairs of differences match, without temporary storage.

Leave a Reply

Your email address will not be published. Required fields are marked *