Common String Method
s.index(x, start, end) # returns smallest i such that s[i]==x.
s.partition
s.strip
s.split
s.index
s.find
s.isalnum
s.isalpha
s.isdigit
s.islower
sep.join(list)
s.count
s.replace
s.startswith
s.endswith
s.swapcase
s.lower
s.upper
ord() # ord('a) == 97
note
there is no
s.sort()
>>> s.sort()
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'str' object has no attribute 'sort'
>>> sorted(s)
['a', 'a', 'd', 'f', 'h', 'h', 'j', 'k', 'k', 'l', 's']
Some edge case of string method:
s.split()
s = 'fhakjhdlksa'
>>> s.split('f')
['', 'hakjhdlksa']
>>> ss = " a "
>>> ss.split()
['a']
>>> ss.split(' ')
['', 'a', '']
>>> ' 1 2 3 '.split()
['1', '2', '3']
s.strip()
>>> ' spacious '.strip()
'spacious'
>>> 'www.example.com'.strip('cmowz.')
'example'
String tricks
Sort a string
''.join(sorted(item))
Sort a list of string
20. Valid Parentheses
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. Need to ignore characters like '.', '@', '*' etc...
def isValid(self, s):
stack = []
dict = {"]":"[", "}":"{", ")":"("}
for char in s:
if char in dict.values():
stack.append(char)
elif char in dict.keys():
if stack == [] or dict[char] != stack.pop():
return False
else:
return False
return stack == []
165. Compare Version Numbers
This is an example of question that highly optimized by Python's syntax.
The basic idea is to convert version numbers into list of integers, and then compare them index by index.
https://leetcode.com/discuss/45331/2-4-lines-python-3-different-ways
**Solution 1: ** Pad with izip_longest with fillvalue=0
def compareVersion(self, version1, version2):
splits = (map(int, v.split('.')) for v in (version1, version2))
return cmp(*zip(*itertools.izip_longest(*splits, fillvalue=0)))
Solution 2:
def compareVersion(self, version1, version2):
v1, v2 = (map(int, v.split('.')) for v in (version1, version2))
d = len(v2) - len(v1)
return cmp(v1 + [0]*d, v2 + [0]*-d)
# [0]*-3 is still [0]
# purpose of this list multiplication is align the length of two list
# cmp can work on list, compare element by element
# I feel this step is not needed, as cmp anyway do comparison element by element
Solution 3
def compareVersion(self, version1, version2):
versions1 = [int(v) for v in version1.split(".")]
versions2 = [int(v) for v in version2.split(".")]
for i in range(max(len(versions1),len(versions2))):
v1 = versions1[i] if i < len(versions1) else 0
v2 = versions2[i] if i < len(versions2) else 0
if v1 > v2:
return 1
elif v1 < v2:
return -1;
return 0;
125. Valid Palindrome
def isPalindrome(self, s):
newS= [i.lower() for i in s if i.isalnum()]
# is.alnum(): Return true if all characters in the string are alphanumeric and there is at least one character, false otherwise.
return newS == newS[::-1]
def isPalindrome(self, s):
l, r = 0, len(s)-1
while l < r:
while l < r and not s[l].isalnum():
l += 1
while l
38. Count and Say
(ok, ignore this question. not useful )
def countAndSay(self, n):
s = ['1']
result = '1'
# The n-th sequance, input 1 should output '1'
for i in range(n-1):
start = 0
temp = []
# Process one sequence, scan from start to end
while start < len(s):
count = 1
next = start + 1
# Scan until s[next] is different
while next < len(s) and s[start] == s[next]:
next += 1
count += 1
# Get the new items in
temp.append(str(count))
temp.append(s[start])
# Start from next one
start = next
# Concatenate list into string, using "," as separator in default
result = ''.join(temp)
s = temp
return result
58. Length of Last Word
def lengthOfLastWord(self, s):
A=s.strip() # if no this step, s = "a " will get wrong
B=A.split(" ") # need to specify " "
return len(B[-1])
I've noticed that a lot of solutions use available library functions that return directly the positions of certain characters or do other operations like "split". I personally don't think that's a good idea. Firstly, these functions take some time and usually involve with iteration through the whole string. Secondly, questions like this one is intended to be a practice of detail implementation, not calling other functions. My solution like below uses only the most basic string operations and probably beats many other solutions which call other existing functions.
// without using build-in function:
int lengthOfLastWord(const char* s) {
int len = 0;
while (*s) {
if (*s++ != ' ')
++len;
else if (*s && *s != ' ')
len = 0;
}
return len;
}
8. String to Integer (atoi)
Implement atoi to convert a string to an integer. atoi() function only works with string format digits, not alphabets.
Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.
Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.
In Python, atoi() is replaced by int() (so don't use int() )
class Solution(object):
def myAtoi(self, s):
"""
:type str: str
:rtype: int
"""
###better to do strip before sanity check (although 8ms slower):
#ls = list(s.strip())
#if len(ls) == 0 : return 0
if len(s) == 0 : return 0
ls = list(s.strip()) # remember this <--- !!!!
# list('abc') == ['a','b','c']
sign = -1 if ls[0] == '-' else 1
if ls[0] in ['-','+'] : del ls[0]
ret, i = 0, 0
while i < len(ls) and ls[i].isdigit() :
ret = ret*10 + ord(ls[i]) - ord('0') # ord('a') == 97
i += 1
return max(-2**31, min(sign * ret,2**31-1))
# this is the max of python integer range
3. Longest Substring Without Repeating Characters
/**
* Solution (DP, O(n)):
*
* Assume L[i] = s[m...i], denotes the longest substring without repeating
* characters that ends up at s[i], and we keep a hashmap for every
* characters between m ... i, while storing in the
* hashmap.
* We know that each character will appear only once.
* Then to find s[i+1]:
* 1) if s[i+1] does not appear in hashmap
* we can just add s[i+1] to hash map. and L[i+1] = s[m...i+1]
* 2) if s[i+1] exists in hashmap, and the hashmap value (the index) is k
* let m = max(m, k), then L[i+1] = s[m...i+1], we also need to update
* entry in hashmap to mark the latest occurency of s[i+1].
*
* Since we scan the string for only once, and the 'm' will also move from
* beginning to end for at most once. Overall complexity is O(n).
*
* If characters are all in ASCII, we could use array to mimic hashmap.
*/
int lengthOfLongestSubstring(string s) {
// for ASCII char sequence, use this as a hashmap
vector charIndex(256, -1);
int longest = 0, m = 0;
for (int i = 0; i < s.length(); i++) {
m = max(charIndex[s[i]] + 1, m); // automatically takes care of -1 case
charIndex[s[i]] = i;
longest = max(longest, i - m + 1);
}
return longest;
}
class Solution:
# @return an integer
def lengthOfLongestSubstring(self, s):
start = maxLength = 0
usedChar = {}
for i in range(len(s)):
if s[i] in usedChar and start <= usedChar[s[i]]:
start = usedChar[s[i]] + 1
else:
maxLength = max(maxLength, i - start + 1)
usedChar[s[i]] = i
return maxLength
** Without hash table **
The idea is pretty simple. We iterate thru the list once and we keep a pointer of where the current longest possible substring starts. During the iteration, we check if this last character is contained in the current substring. If so, move the ptr to the first index of the char at the string +1. Everytime we will compare and keep the max value up to date.
Overall time complexity will be O(n^2)
public class Solution {
public int lengthOfLongestSubstring(String s) {
if (s.length() <= 1) return s.length();
int max = 1;
int ptr = 0;
for (int i = 1; i< s.length(); i++) {
// find the first occurence of the char after index ptr
int index = s.indexOf(s.charAt(i), ptr);
if (index < i) { // it means that it is contained in s.substring(ptr, i)
ptr = index + 1;
}
max = Math.max(max, i - ptr + 1);
}
return max;
}
}
# without dictionary:
def lengthOfLongestSubstring (self, inString):
chDict=[-1]*256
start=0
maxLen=0
for i in xrange(len(inString)):
asc=ord(inString[i])
if chDict[asc]==-1 or chDict[asc]
5. Longest Palindromic Substring
Basic thought is simple. when you increase s by 1 character, you could only increase maxPalindromeLen by 1 or 2, and that new maxPalindrome includes this new character. Proof: if on adding 1 character, maxPalindromeLen increased by 3 or more, say the new maxPalindromeLen is Q, and the old maxPalindromeLen is P, and Q>=P+3. Then it would mean, even without this new character, there would be a palindromic substring ending in the last character, whose length is at least Q-2. Since Q-2 would be >P, this contradicts the condition that P is the maxPalindromeLen without the additional character.
So, it becomes simple, you only need to scan from beginning to the end, adding one character at a time, keeping track of maxPalindromeLen, and for each added character, you check if the substrings ending with this new character, with length P+1 or P+2, are palindromes, and update accordingly.
Now, this is O(n^2) as taking substrings and checking palindromicity seem O(n) time.
class Solution:
# @return a string
def longestPalindrome(self, s):
if len(s)==0:
return 0
maxLen=1
start=0
for i in xrange(len(s)):
if i-maxLen >=1 and s[i-maxLen-1:i+1]==s[i-maxLen-1:i+1][::-1]:
start=i-maxLen-1
maxLen+=2
continue
if i-maxLen >=0 and s[i-maxLen:i+1]==s[i-maxLen:i+1][::-1]:
start=i-maxLen
maxLen+=1
return s[start:start+maxLen]
# a not very smart solution
def longestPalindrome(self, s):
res = ""
for i in xrange(len(s)):
# odd case, like "aba"
tmp = self.helper(s, i, i)
if len(tmp) > len(res):
res = tmp
# even case, like "abba"
tmp = self.helper(s, i, i+1)
if len(tmp) > len(res):
res = tmp
return res
# get the longest palindrome, l, r are the middle indexes
# from inner to outer
def helper(self, s, l, r):
while l >= 0 and r < len(s) and s[l] == s[r]:
l -= 1; r += 1
return s[l+1:r]
49. Group Anagrams
????
def anagrams(self, strs):
dic = defaultdict(list)
map(lambda item: dic[''.join(sorted(item))].append(item), strs)
return [x for key in dic.keys() for x in dic[key] if len(dic[key]) > 1]
This is equivalent to:
def anagrams(self, strs):
dic = defaultdict(list)
for item in strs:
after = ''.join(sorted(item))
dic[after].append(item)
ans = []
for item in dic:
values = dic[item]
if len(values) > 1:
ans.extend(values)
return ans
14. Longest Common Prefix (S)
# hard to think solution
class Solution:
# @return a string
def longestCommonPrefix(self, strs):
if not strs:
return ""
for i, letter_group in enumerate(zip(*strs)):
if len(set(letter_group)) > 1:
# very tricky step: see python notebook example to further understand this !!!
# set() will remove duplicate, if there is difference (not common), then len(set(letter_group)) will > 1
return strs[0][:i]
else:
return min(strs)
# relatively easy to think solution
class Solution:
# return common prefix for two strings:
def lcp(self, str1, str2):
i = 0
while (i < len(str1) and i < len(str2)):
if str1[i] == str2[i]:
i = i+1
else:
break
return str1[:i]
# @return a string
# return common prefix for all strings
# build upon the common prefix for two strings:
def longestCommonPrefix(self, strs):
if not strs:
return ''
else:
return reduce(self.lcp,strs) # remember this trick:
# use reduce function to roll over all the element
6. ZigZag Conversion
remember this solution
class Solution(object):
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
if numRows == 1 or numRows >= len(s):
return s
L = [''] * numRows # list
index, step = 0, 1
for x in s:
L[index] += x
if index == 0:
step = 1
elif index == numRows -1:
step = -1
index += step
return ''.join(L) # list -> string
67. Add Binary
Both input and return are binary string.
if int() and bin() function is allowed:
def addBinary(self, a, b):
a = int(a, 2) # convert binary string into decimal integer
b = int(b, 2)
return ("" + bin(a+b))[2:] # bin() convert decimal integer into binary string, with leading '0b'
Iterative without using int():
# iterative backward:
def addBinary(self, a, b):
res, carry = '', 0
i, j = len(a) - 1, len(b) - 1
while i >= 0 or j >= 0 or carry:
curval = (i >= 0 and a[i] == '1') + (j >= 0 and b[j] == '1')
# Cool !!
carry, rem = divmod(curval + carry, 2)
# return a pair of numbers consisting of their quotient and remainder when using long division
res = `rem` + res
i -= 1
j -= 1
return res
Another iterative without int(), easy to understand:
def addBinary(self, a, b):
i, m, n, result, carry = 1, len(a), len(b), [], 0
while i <= m or i <= n:
temp = carry
if i <= m:
temp += int(a[-i])
if i <= n:
temp += int(b[-i])
carry = temp / 2
result.append(str(temp % 2))
i += 1
if carry:
result.append(str(carry))
return ''.join(result[::-1])
recursive, without using int():
(remember how this function deal with return in recursion)
class Solution:
# smart approach!! just some manipulation of string
def addBinary(self, a, b):
if len(a)==0: return b
if len(b)==0: return a
if a[-1] == '1' and b[-1] == '1':
return self.addBinary(self.addBinary(a[0:-1],b[0:-1]),'1')+'0' # Remember !!!
if a[-1] == '0' and b[-1] == '0':
return self.addBinary(a[0:-1],b[0:-1])+'0'
else:
return self.addBinary(a[0:-1],b[0:-1])+'1'
28. Implement strStr()
class Solution(object):
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
# iterative by index, not iterator
for i in range(len(haystack) - len(needle)+1):
if haystack[i:i+len(needle)] == needle:
return i
return -1
227. Basic Calculator II
This is not as easy as first look
def calculate(self, s):
if not s:
return "0"
stack, num, sign = [], 0, "+"
for i in xrange(len(s)):
if s[i].isdigit():
num = num*10+ord(s[i])-ord("0")
if (not s[i].isdigit() and not s[i].isspace()) or i == len(s)-1:
if sign == "-":
stack.append(-num)
elif sign == "+":
stack.append(num)
elif sign == "*":
stack.append(stack.pop()*num)
else:
tmp = stack.pop()
if tmp//num < 0 and tmp%num != 0:
stack.append(tmp//num+1)
else:
stack.append(tmp//num)
sign = s[i]
num = 0
return sum(stack)