-1

Given a string s, return true if the s can be palindrome after deleting at most one character from it.

Example 1:

Input: s = "aba"
Output: true

Example 2:

Input: s = "abca"
Output: true
Explanation: You could delete the character 'c'.

Example 3:

Input: s = "abc"
Output: false

For this problem in leetcode my code has passed 462/469 test cases:

Following is the test case for which my code is failing the test.

"aguokepatgbnvfqmgmlcupuufxoohdfpgjdmysgvhmvffcnqxjjxqncffvmhvgsymdjgpfdhooxfuupuculmgmqfvnbgtapekouga"

My code is:

class Solution:
    def validPalindrome(self, s: str) -> bool:
        
        skip=0
        l,r=0,len(s)-1
        
        while l<r:
            if s[l]==s[r]:
                l+=1
                r-=1
            elif s[l]!=s[r] and skip<1 and s[l+1]==s[r]:
                l+=1
                skip=1
            elif s[l]!=s[r] and skip<1 and s[r-1]==s[l]:
                r-=1
                skip=1
            else:
                return False
        return True

What is the problem with my code?

Note: in this string the output should be true, mine returns false

From left there are characters 'lcup' and from right there are characters 'lucup' My code is supposed to skip the letter u from right side and continue.

"aguokepatgbnvfqmgm**lcup**uufxoohdfpgjdmysgvhmvffcnqxjjxqncffvmhvgsymdjgpfdhooxfuu**pucul**mgmqfvnbgtapekouga"

Another example: It returns true for the following string: s='adau'

Skips letter 'u' as expected.

However when I use the example according to the test case string that failed, it returns False. s= 'cuppucu'

It should skip first u from the right side and return True but it doesn't.

However as soon as I replace that last letter 'u' with letter 'a' it skips the letter 'a' and returns True. What's the problem here?

redox741
  • 21
  • 5
  • And does it skip the letter u from the right side and continue? – mkrieger1 Oct 30 '22 at 23:12
  • The code is working fine for the strings that I checked in my local machine. For example s='acda'. It skips the letter d and continue and returns True – redox741 Oct 30 '22 at 23:15
  • Questions seeking debugging help (**"why isn't this code working?"**) should include the desired behavior, *a specific problem or error* and *the shortest code necessary* to reproduce it *as formatted text* (not images) **in the question itself**. Questions without **a clear problem statement** are not useful to other readers. We have no idea what you're trying to do other than it involves palindromes. Please [edit] your post and put a complete problem statement in it. – MattDMo Oct 30 '22 at 23:19
  • Another example: It returns true for the following string: s='adau' Skips letter 'u' as expected. However when I use the example according to the test case string that failed, it returns False. s= 'cuppucu' It should skip first u from the right side and return True but it doesn't, however as soon as I replace that last letter 'u' with letter 'a' it skips the letter 'a' and returns True. What's the problem here? – redox741 Oct 30 '22 at 23:19
  • It's a problem with your algorithm. If you switch the order of the two 'elif' statements it will work, but now it might fail with other test cases. – Matthias Oct 30 '22 at 23:43
  • Yes it did work for this testcase that failed after I switched the order of two 'elifs. But failed for 5 other testcases, 464/469. But why does the order of elif matter, its supposed to check the condition for both the elif and execute if any of the elifs are true right? – redox741 Oct 30 '22 at 23:49

2 Answers2

0

Imagine the string 'abbab'. First you check index 0 and 4 with the values "a" and "b". They are not the same, but the "b" at index 1 matches the "b" at index 4. So you move forward to 1 and 4. Next is 2 and 3 and those are "b" and "a" and those don't match too. End of the story.

abbab
l   r

abbab
 l  r

abbab
  lr

-> False

Now let's switch around the elif blocks. Again you check index 0 and 4 first. They are not the same, so you now change the right index first and see that 0 and 3 are both "a". The next comparison is at indexes 1 and 2 with "b". Finished. Found a match.

abbab
l   r

abbab
l  r

abbab
 lr

-> True

You didn't ask for it but here is a working solution with a different approach.

class Solution:
    def generate_strings(self, s):
        yield s, s[::-1]
        for pos in range(len(s)):
            t = ''.join(c for i, c in enumerate(s) if i != pos)
            yield t, t[::-1]

    def validPalindrome(self, p):
        return any(x == y for x, y in self.generate_strings(p))
Matthias
  • 12,873
  • 6
  • 42
  • 48
  • Right, that makes sense. Any suggestion how can I modify my code to work? – redox741 Oct 31 '22 at 00:10
  • Thank you for the solution, I'm aware of this solution but I was trying to come up with a solution with O(1) memory. – redox741 Oct 31 '22 at 00:11
  • Your solution would be faster - if it would work. At the moment I have no idea how to fix that approach. – Matthias Oct 31 '22 at 00:14
  • I think you will have to check if both possibilities are valid. Explore one further and store the indexes of the other in a list (because that might happen multiple times). If the result is false, then repeat the process with the next value in the list (and remove the current entry) until the list is empty. – Matthias Oct 31 '22 at 00:18
0

I over-complicated this in my first answer. I thought that you had to skip a particular character multiple times. As others have pointed out, that isn't true. So you have a solution from someone else, but you wanted to know how to change your code to always do the right thing. One way would be to run your algorithm twice. The first time, you only consider if you can skip a character on the left side of the input string as you're walking over it. For the second call, you only consider skipping a character on the right side. For cases like the one you ran into here where you could choose to skip either character, but only one will produce a positive result, well then if you try both cases, one or the other will always succeed when it should.

So here's that simple change you can make...modifying your function only slightly, and then calling it twice.

class Solution:

    def _validPalindrome(self, s: str, choose_first: bool) -> bool:
        skip = 0
        l, r = 0, len(s) - 1
        while l < r:
            if s[l] == s[r]:
                l += 1
                r -= 1
            elif choose_first and s[l] != s[r] and skip < 1 and s[r - 1] == s[l]:
                r -= 1
                skip = 1
            elif not choose_first and s[l] != s[r] and skip < 1 and s[l + 1] == s[r]:
                l += 1
                skip = 1
            else:
                return False
        return True

    def validPalindrome(self, s: str) -> bool:
        return self._validPalindrome(s, False) or self._validPalindrome(s, True)

def main():
    inp = "aguokepatgbnvfqmgmlcupuufxoohdfpgjdmysgvhmvffcnqxjjxqncffvmhvgsymdjgpfdhooxfuupuculmgmqfvnbgtapekouga"
    print(Solution().validPalindrome(inp))

main()

Result:

True

This should pass for all the cases for which your original code passed.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
  • This could work perfectly but again we are back to the problem which I have been trying to solve, here we are using recursion so memory complexity will not be O(1) right? So basically there is no way to solve this problem with O(1) memory.? – redox741 Oct 31 '22 at 00:28
  • Okay, nevermind we are only using recursion once so memory should be O(1) I'm assuming. – redox741 Oct 31 '22 at 00:30
  • Yes, this isn't really recursion. Calling a function twice isn't recursion. This could be done more efficiently by not having to walk the beginning of the string (up to the first mismatch) twice, but in most real life cases, such optimization is unnecessary as it will make no real difference to the overall problem. This is still O(1). Glad this helped! – CryptoFool Oct 31 '22 at 00:34