"""
Given a string, determine if it is a palindrome,
considering only alphanumeric characters and ignoring cases.

For example,
"A man, a plan, a canal: Panama" is a palindrome.
"race a car" is not a palindrome.

Note:
Have you consider that the string might be empty?
This is a good question to ask during an interview.

For the purpose of this problem,
we define empty string as valid palindrome.
"""


def is_palindrome(s):
    """
    :type s: str
    :rtype: bool
    """
    i = 0
    j = len(s)-1
    while i < j:
        while i < j and not s[i].isalnum():
            i += 1
        while i < j and not s[j].isalnum():
            j -= 1
        if s[i].lower() != s[j].lower():
            return False
        i, j = i+1, j-1
    return True


"""
Here is a bunch of other variations of is_palindrome function.

Variation 1:
Find the reverse of the string and compare it with the original string

Variation 2:
Loop from the start to length/2 and check the first character and last character
and so on... for instance s[0] compared with s[n-1], s[1] == s[n-2]...

Variation 3:
Using stack idea. 
Note: We are assuming that we are just checking a one word string. To check if a complete sentence 

"""    
def remove_punctuation(s):
	"""
	Remove punctuation, case sensitivity and spaces
	"""
    return "".join(i.lower() for i in s if i in string.ascii_letters)

# Variation 1
def string_reverse(s):
	return s[::-1]

def is_palidrome_reverse(s):
	s = remove_punctuation(s)
	reversed_string = string_reverse(s)
	
	# can also get rid of the string_reverse function and just do this return s == s[::-1] in one line.
	if (s == reversed_string): 
		return True
	return False	


# Variation 2
def is_palidrome_two_pointer(s):
	s = remove_punctuation(s)
	
	for i in range(0 : len(s)/2):
		if (s[i] != s[len(s) - i - 1]):
			return False
	return True
	
	for i in range(0, len(s)//2):
		if (s[i] != s[len(s) - i - 1]):
			return False
	return True
	
# Variation 3
def is_palindrome_stack(s):
	stack = []
	s = remove_punctuation(s)
	
	for i in range(len(s)//2, len(s)):
		stack.append(s[i])
	for i in range(0, len(s)//2):
		if s[i] != stack.pop():
			return False
	return True	

@@ -107,17 +107,16 @@
  cases.%0A
-%0A
 For exam
@@ -204,17 +204,16 @@
 ndrome.%0A
-%0A
 Note:%0AHa
@@ -312,17 +312,16 @@
 erview.%0A
-%0A
 For the 
@@ -393,16 +393,49 @@
 me.%0A%22%22%22%0A
+from string import ascii_letters%0A
 %0A%0Adef is
@@ -777,17 +777,16 @@
 n True%0A%0A
-%0A
 %22%22%22%0AHere
@@ -1128,16 +1128,17 @@
  idea. %0A
+%0A
 Note: We
@@ -1233,16 +1233,13 @@
 ce %0A
-%0A
 %22%22%22  
-  
 %0Adef
@@ -1266,14 +1266,20 @@
 s):%0A
-%09%22%22%22%0A%09
+    %22%22%22%0A    
 Remo
@@ -1322,17 +1322,20 @@
  spaces%0A
-%09
+    
 %22%22%22%0A    
@@ -1378,23 +1378,16 @@
 if i in 
-string.
 ascii_le
@@ -1747,25 +1747,28 @@
 pointer(s):%0A
-%09
+    
 s = remove_p
@@ -1775,33 +1775,36 @@
 unctuation(s)%0A%09%0A
-%09
+    
 for i in range(0
@@ -1807,18 +1807,17 @@
 ge(0
- :
+,
  len(s)/
 2):%0A
@@ -1812,22 +1812,29 @@
  len(s)/
+/
 2):%0A
-%09%09
+        
 if (s%5Bi%5D
@@ -1861,107 +1861,20 @@
 %5D):%0A
-%09%09%09return False%0A%09return True%0A%09%0A%09for i in range(0, len(s)//2):%0A%09%09if (s%5Bi%5D != s%5Blen(s) - i - 1%5D):%0A%09%09%09
+            
 retu
@@ -1874,33 +1874,36 @@
    return False%0A
-%09
+    
 return True%0A%09%0A# 
@@ -1900,16 +1900,17 @@
  True%0A%09%0A
+%0A
 # Variat
@@ -1943,17 +1943,20 @@
 ack(s):%0A
-%09
+    
 stack = 
@@ -1958,17 +1958,20 @@
 ck = %5B%5D%0A
-%09
+    
 s = remo
@@ -1990,17 +1990,20 @@
 on(s)%0A%09%0A
-%09
+    
 for i in
@@ -2029,18 +2029,24 @@
 en(s)):%0A
-%09%09
+        
 stack.ap
@@ -2056,17 +2056,20 @@
 d(s%5Bi%5D)%0A
-%09
+    
 for i in
@@ -2090,18 +2090,24 @@
 s)//2):%0A
-%09%09
+        
 if s%5Bi%5D 
@@ -2126,11 +2126,20 @@
 ():%0A
-%09%09%09
+            
 retu
@@ -2147,17 +2147,20 @@
 n False%0A
-%09
+    
 return T

fix the error in is_palindrome (#249)