美文网首页
String Question Summary

String Question Summary

作者: abrocod | 来源:发表于2016-03-03 10:10 被阅读81次

    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 "<stdin>", line 1, in <module>
    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 <r and not s[r].isalnum():
                r -= 1
            if s[l].lower() != s[r].lower():
                return False
            l +=1; r -= 1
        return True
    

    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 <character, index> 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<int> 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]<start:
                chDict[asc]=i
            else:
                if maxLen<i-start:
                    maxLen=i-start
                start=chDict[asc]+1
                chDict[asc]=i
    
        return max(maxLen, len(inString)-start)  
    

    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)
    

    相关文章

      网友评论

          本文标题:String Question Summary

          本文链接:https://www.haomeiwen.com/subject/sknnkttx.html