def getValueFromStr(str1): """给定字符串,字符串表示一个公式,可能有整数、加减乘除符号和左右括号,返回公式计算结果""" return valueProcess(str1, 0)[0] # 返回两个值:计算结果,计算到的位置 def valueProcess(str1, index): pre = 0 que = [] while index < len(str1) and str1[index] != ')': if '0' <= str1[index] <= '9': pre = pre * 10 + int(str1[index]) index += 1 elif str1[index] != "(": # str1[index] 此时是 +-*/ addNum(que, pre) que.append(str1[index]) index += 1 pre = 0 else: # str1[index] 此时为 '(' bra = valueProcess(str1, index + 1) pre = bra[0] index = bra[1] + 1 addNum(que, pre) return getResult(que), index def addNum(que, num): if que != []: topStr = que.pop() if topStr in '+-': que.append(topStr) else: cur = int(que.pop()) num = cur * num if topStr == '*' else cur // num que.append(str(num)) def getResult(que): res = 0 addFlag = True while que != []: cur = que.pop(0) if cur == '+': addFlag = True elif cur == '-': addFlag = False else: res += int(cur) if addFlag else -int(cur) return res class SkipListNode(object): """跳表节点""" def __init__(self, value): self.value = value # 列表中存储的是节点,nextNodes[1]指的是这一层所指向的下一个节点是什么 self.nextNodes = [] class SkipList(object): def __init__(self): self.size = 0 # 加入了多少个key self.maxLevel = 0 # 所有数据随机出的最大层 self.head = SkipListNode(None) # 巨小,整个结构最前面的链,层数与所有数据随机出的最大层一样 self.head.nextNodes.append(None) self.PROBABILITY = 0.5 def add(self, newValue): if not self.contains(newValue): self.size += 1 level = 0 while random.random() < self.PROBABILITY: level += 1 while level > self.maxLevel: self.head.nextNodes.insert(0, None) self.maxLevel += 1 newNode = SkipListNode(newValue) current = self.head while level >= 0: current = self.findNext(newValue, current, level) newNode.nextNodes.insert(0, current.nextNodes[level]) current.nextNodes[level] = newNode level -= 1 def findNext(self, e, current, level): nextNode = current.nextNodes[level] while nextNode is not None: value = nextNode.value if self.lessThan(e, value): break current = nextNode nextNode = current.nextNodes[level] return current def contains(self, value): node = self.findNode(value) return node is not None and node.value is not None and node.value == value def findNode(self, e): return self.find(e, self.head, self.maxLevel) def lessThan(self, a, b): return a < b def find(self, e, current, level): while level >= 0: current = self.findNext(e, current, level) level -= 1 return current def delete(self, deleteValue): if self.contains(deleteValue): deleteNode = self.findNode(deleteValue) self.size -= 1 level = self.maxLevel current = self.head while level >= 0: current = self.findNext(deleteNode.value, current, level) if deleteNode.nextNodes.size > level: current.nextNodes[level] = deleteNode.nextNodes[level] level -= 1 def getMaxEor1(alist): maxNum, eor = 0, 0 dp = [0] * len(alist) for i in range(len(alist)): eor ^= alist[i] maxNum = max(maxNum, eor) for start in range(1, i + 1): curEor = eor ^ dp[start - 1] # eor(alist[start:i]) = eor(alist[:i]) ^ eor(alist[:start-1]) maxNum = max(maxNum, curEor) dp[i] = eor return maxNum class NumTrieNode(object): def __init__(self): self.nexts = [None] * 2 class NumTrie(object): def __init__(self): self.head = NumTrieNode() def add(self, num): curNode = self.head for move in range(31, -1, -1): path = (num >> move) & 1 # 从高位依次取数 curNode.nexts[path] = NumTrieNode() if curNode.nexts[path] is None else curNode.nexts[path] curNode = curNode.nexts[path] # 为了求以i为结尾最大的异或和,假设从start开始到i结束的异或和是最大的。 # 由公式eor(alist[start:i]) = eor(alist[:i]) ^ eor(alist[:start-1]),但由于k无法得知, # 而eor(alist[:i])是从头开始异或,是已知的,因此可以按照已知的eor(alist[:i])推测当eor(alist[:start-1])是什么时, # 可以使eor(alist[start:i])成为最大的。而eor(alist[start:i])最好的结果是符号位是0,其他位均为1。 # 即使最高位被迫只能为1(因为eor(alist[:start-1])全都是以1开始的)时,其他位也最好能为1,此时是最大的(负数是以补码的形式存在的) def maxXor(self, num): curNode = self.head res = 0 for move in range(31, -1, -1): path = (num >> move) & 1 # 从高位一次取数 # 为了使数最大,最高位符号位最好的结果是0(正数),其他位最好的结果是1。 best = path if move == 31 else path ^ 1 # 期待要选的路 # 如果有理想的数字通道,当然按照理想的走,但如果实际并没有,只能按实际来,下一位在试,这样是最大的 best = best if curNode.nexts[best] is not None else best ^ 1 # 实际选择的路 res |= (path ^ best) << move # 设置答案的每一位 curNode = curNode.nexts[best] return res def maxXorSubArray(alist): if alist == [] or len(alist) == 0: return 0 maxNum, eor = 0, 0 numTrie = NumTrie() numTrie.add(0) for i in range(len(alist)): eor ^= alist[i] maxNum = max(maxNum, numTrie.maxXor(eor)) numTrie.add(eor) return maxNum # 换钱的方法数,给定数组arr,arr中所有值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张, # 再给定一个整数aim代表要找的钱数,求换钱有多少种方法 def coins(alist, aim): if alist == [] or len(alist) == 0 or aim < 0: return 0 # return coinsProcess(alist, 0, aim) return coinsProcess_map(alist, 0, aim) def coinsProcess(alist, index, aim): """ :param alist: 给定的正数数组 :param index: 可以任意自由使用index及其以后所有的钱 :param aim: 目标钱数 :return: 方法数 """ res = 0 if index == len(alist): res = 1 if aim == 0 else 0 else: i = 0 while i * alist[index] <= aim: res += coinsProcess(alist, index + 1, aim - i * alist[index]) i += 1 return res # 无后效性:如果到达这个状态,和这个状态以后要完成的工作无影响。 # 比如[200, 100, 50, 10],目标金额1000,假设index=2,此时目标金额为600,使用index以后的金钱实现目标金额600的方法数和 # 如何达到index=2,aim=600的这种状态无关,即通过alist数组前2个数实现400这个过程和通过alist数组后2个数实现600这两个过程 # 互不影响、无关联。 # 一张200,两张100,后面需要完成600的目标,和两张200,没有100,后面完成600的目标。两个方法中,通过后两个实现600的目标方法数是一定的。 # 可以将其放在一个字典中,极大的节省了暴力递归时间 def coinsProcess_map(alist, index, aim): """ 记忆化搜索 :param alist: 给定的正数数组 :param index: 可以任意自由使用index及其以后所有的钱 :param aim: 目标钱数 :return: 方法数 """ mapDict = {} # key: "index_aim" value: 返回值 res = 0 if index == len(alist): res = 1 if aim == 0 else 0 else: i = 0 while i * alist[index] <= aim: nextAim = aim - i * alist[index] keyStr = str(index + 1) + '_' + str(nextAim) if keyStr in mapDict.keys(): res += mapDict[keyStr] else: res += coinsProcess_map(alist, index + 1, aim - i * alist[index]) i += 1 mapDict.update({str(index) + "_" + str(aim): res}) return res # 完全由暴力递归改为动态规划 def coins2(alist, aim): if alist == [] or len(alist) == 0 or aim < 0: return 0 dp = np.zeros((len(alist) + 1, aim + 1)) for i in range(1, aim + 1): dp[len(alist)][i] = 0 # 由终止条件确定。已经到了数组末尾,而aim>0,已经没有整数可用,仍想配成一定金额,所以方法数为0 dp[len(alist)][0] = 1 # 由终止条件确定。当数组的数已经用完,此时目标数同时为0,返回方法数为1 for i in range(len(alist) - 1, -1, -1): for j in range(0, aim + 1): k = 0 while j - k * alist[i] >= 0: dp[i][j] += dp[i + 1][j - k * alist[i]] k += 1 return dp[0][aim] # 对初始版的动态规划优化一丢丢 def coins3(alist, aim): if alist == [] or len(alist) == 0 or aim < 0: return 0 dp = np.zeros((len(alist) + 1, aim + 1)) for i in range(aim + 1): dp[len(alist)][i] = 0 dp[len(alist)][0] = 1 for i in range(len(alist) - 1, -1, -1): for j in range(0, aim + 1): dp[i][j] = dp[i + 1][j] # dp[i][j] = dp[i + 1][j] + dp[i + 1][j - alist[i]] + dp[i + 1][j - 2 * alist[i]] + ... # dp[i][j - alist[i]] = dp[i + 1][j - alist[i]] + dp[i + 1][j - 2 * alist[i]] + ... # 因此 dp[i][j] = dp[i + 1][j] + dp[i][j - alist[i]] dp[i][j] += dp[i][j - alist[i]] if j - alist[i] >= 0 else 0 return dp[0][aim] def coins4(alist, aim): if alist == [] or len(alist) == 0 or aim < 0: return 0 dp = np.zeros((len(alist), aim + 1)) for i in range(len(alist)): dp[i][0] = 1 j = 1 # 不需要终止行,在第一行,只需要是5的倍数的aim,都可以实现,方法数仅是1(因为只用了一种金额的) while alist[0] * j <= aim: dp[0][alist[0] * j] = 1 j += 1 for i in range(1, len(alist)): for j in range(1, aim + 1): dp[i][j] = dp[i - 1][j] dp[i][j] += dp[i][j - alist[i]] if j - alist[i] >= 0 else 0 return dp[len(alist) - 1][aim] # 排成一条线的纸牌博弈问题 def winCard1(alist): if alist == [] or len(alist) == 0: return 0 return max(f(alist, 0, len(alist) - 1), s(alist, 0, len(alist) - 1)) # alist[i:j]这个排列的纸牌被绝顶聪明的人先拿,最终能获得什么分数 def f(alist, i, j): """alist[i:j]这个排列的纸牌被绝顶聪明的人先拿,最终能获得什么分数""" if i == j: # 如果仅剩一张,当然会被先拿的人拿走 return alist[i] # 先拿的人拿的是alist[i],或者是alist[j],此时后拿的人因为同样是绝顶聪明的人,所以调用s()函数 return max(alist[i] + s(alist, i + 1, j), alist[j] + s(alist, i, j - 1)) def s(alist, i, j): """alist[i:j]这个排列的纸牌被绝顶聪明的人后拿,最终能获得什么分数""" if i == j: # 如果仅剩一张,肯定会被先拿的人拿走,后拿的人什么也拿不到 return 0 # 由于对手先拿,由于对方也是绝顶聪明的人,肯定会将最差的情况留给玩家。在对方拿完之后,此时玩家成了先拿的人 return min(f(alist, i + 1, j), f(alist, i, j - 1)) def winCard2(alist): """由暴力递归改成动态规划""" if alist == [] or len(alist) == 0: return 0 dpF = np.zeros((len(alist), len(alist))) dpS = np.zeros((len(alist), len(alist))) for j in range(len(alist)): dpF[j][j] = alist[j] for i in range(j - 1, -1, -1): dpF[i][j] = max(alist[i] + dpS[i + 1][j], alist[j] + dpS[i][j - 1]) dpS[i][j] = min(dpF[i + 1][j], dpF[i][j - 1]) return max(dpF[0][len(alist) - 1], dpS[0][len(alist) - 1]) # 机器人处于位置M(1<=M<=N)时,既可以向左走,也可以向右走(M=1时,只能向右,M=N时,只能向左)。经过P步后,走到位置K,一共有多少种方法 def ways(N, curPosition, restSteps, K): """ :param N: 一共有1~N的位置 :param curPosition: :param restSteps: 剩余走的步数 :param K: 最终停留的位置 :return: 一共有多少种走法 """ if N < 2 or curPosition < 1 or curPosition > N or restSteps < 0 or K < 1 or K > N: return 0 if restSteps == 0: return 1 if curPosition == K else 0 if curPosition == 1: res = ways(N, curPosition + 1, restSteps - 1, K) elif curPosition == N: res = ways(N, curPosition - 1, restSteps - 1, K) else: res = ways(N, curPosition + 1, restSteps - 1, K) + ways(N, curPosition - 1, restSteps - 1, K) return res def ways2(N, curPosition, restSteps, K): if N < 2 or restSteps < 0 or K < 1 or K > N: return 0 dp = np.zeros((restSteps + 1, N)) dp[0][K - 1] = 1 for i in range(1, restSteps + 1): for j in range(0, N): dp[i][j] = dp[i - 1][j - 1] if j - 1 >= 0 else 0 dp[i][j] += dp[i - 1][j + 1] if j + 1 < N else 0 return dp[restSteps][curPosition - 1] def getMaxLength(alist, aim): if alist == [] or len(alist) == 0 or aim <= 0: return 0 leftPoint, rightPoint, maxLen = 0, 0, 0 sum = alist[0] while rightPoint < len(alist): if sum < aim: rightPoint += 1 if rightPoint == len(alist): break sum += alist[rightPoint] elif sum == aim: maxLen = max(maxLen, rightPoint - leftPoint + 1) sum -= alist[leftPoint] leftPoint += 1 else: sum -= alist[leftPoint] leftPoint += 1 return maxLen def getMinSteps(eggNum, floorNum): """ 扔鸡蛋问题 :param eggNum: :param floorNum: :return: """ if eggNum < 1 or floorNum < 1: return 0 dp = np.zeros((eggNum + 1, floorNum + 1)) for i in range(1, eggNum + 1): for j in range(1, floorNum + 1): dp[i][j] = j for i in range(2, eggNum + 1): for j in range(1, floorNum + 1): for k in range(1, j): dp[i][j] = min(dp[i][j], 1 + max(dp[i - 1][k - 1], dp[i][j - k])) return dp # 从0位置开始向右扩,假如扩到位置T时,仍满足累加和<sum。但加上minSumList[T+1]时,>sum。此时不是从1位置重新开始, # 而是在原有的基础上减去alist[1],看此时能否将minSumList[T+1]加上,因为我们求的是最长子数组的长度。这样做保证了右边界始终向右, # 左边界也一直向右移动,做到了O(N). def maxLengthAwesom(alist, aim): if alist == []: return 0 if len(alist) == 1: return alist[0] # minSumList[i]: i开头的所有子数组的最小累加和 # minSumIndexList[i]: 取得最小累加和的右边界 minSumList, minSumIndexList = [], [] minSumList.append(alist[-1]) minSumIndexList.append(len(alist) - 1) for i in range(len(alist) - 2, -1, -1): if minSumList[0] < 0: minSumList.insert(0, alist[i] + minSumList[0]) minSumIndexList.insert(0, minSumIndexList[0]) else: minSumList.insert(0, alist[i]) minSumIndexList.insert(0, i) end, sumNum, res = 0, 0, 0 for i in range(len(alist)): while end < len(alist) and sumNum + minSumList[end] <= aim: sumNum += minSumList[end] end = minSumIndexList[end] + 1 sumNum -= alist[i] if end > i else 0 res = max(res, end - i) end = max(end, i + 1) return res # 当仅剩最后一个时,修改其编号为1,因此,由公式推导它对应的在最一开始的编号。 # 旧编号(链表长度长度为i)和新编号(链表长度为i-1)对应的公式:旧 = (新 - 1 + S) % i + 1(S:被杀编号) # 编号 = (报数 - 1) % i + 1 # 被杀编号:S = (m - 1) % i + 1 # --> 旧 = (新 + m - 1) % i + 1 # 当仅剩最后一个时,修改其编号为1,因此,由公式推导它对应的在最一开始的编号。 def getLive(Len, m): """ :param Len: 总人数 :param m: 报到m就杀人 :return: 最后一个人 """ if Len == 1: return 1 return (getLive(Len - 1, m) + m - 1) % Len + 1 # 环形单链表的约瑟夫问题,链表长度N,时间复杂度O(N) def josephusKill2(pHead, m): if pHead is None or pHead.next == pHead or m < 1: return pHead cur = pHead.next tmp = 1 # 链表长度 while cur != pHead: tmp += 1 cur = cur.next tmp = getLive(tmp, m) # 由最后一个生存者编号为1,根据公式回推在初始链表中的编号 tmp -= 1 while tmp != 0: pHead = pHead.next tmp -= 1 pHead.next = pHead return pHead def isMatch(str1, str2): """带有‘.’和‘*’的字符串匹配问题""" if str1 == "" or str2 == "": return False strList1, strList2 = [], [] for i in str1: strList1.append(i) for i in str2: strList2.append(i) # return processMatchStr(strList1, strList2, 0, 0) if isValidStr(str1, str2) else False return processMatchStr(strList1, strList2, 0, 0) and isValidStr(str1, str2) def isValidStr(str1, str2): """检查两个字符串的有效性,str1字符串不能有.和*,str2字符串首字符不能是*,不能连着两个字符都是*""" for i in str1: if i in '.*': return False if str2[0] == '*': return False for i in range(len(str2) - 1): if str2[i] == '*' and str2[i + 1] == '*': return False return True def processMatchStr(strList1, strList2, start1, start2): if start2 == len(strList2): return start1 == len(strList1) if start2 + 1 == len(strList2) or strList2[start2 + 1] != '*': """start2后面接的不是'*'(或者start2已经指向了最后一个字符,或是start2后面的字符不是'*') 在这种情况下,必须是三种情况都满足才会满足True。 start1并没有越界 此时两个字符串对应位置的字符相等,或是str2字符串对应的位置为'.'可以匹配任意的字符 在start1,start2位置之后的字符串都能匹配上""" return start1 != len(strList1) and (strList1[start1] == strList2[start2] or strList2[start2] == '.') \ and processMatchStr(strList1, strList2, start1 + 1, start2 + 1) """此时,start2+1位置是'*',分为两种情况讨论 如果start1位置的字符和start2位置的字符不匹配,则start2+1位置的'*'起的作用是0个之前的字符,然后由start2+2位置的字符继续与start1位置的字符继续考虑匹配问题 假设此时从start1位置开始的str1是:a...,从start2位置开始的str2是b*..., 如果start1位置的字符和start2位置的字符匹配,则需要考虑start2+1位置的'*'所起的作用,是当做1倍、2倍、3倍...之前的字符进行匹配""" while start1 != len(strList1) and (strList1[start1] == strList2[start2] or strList2[start2] == '.'): """假设此时从start1位置开始的str1是:aaab...,从start2位置开始的str2是a*... 首先考虑'*'只当做0倍,也就是str1从start1开始匹配,str2从start2+2开始匹配,如果不匹配 则考虑'*'当做1倍考虑,也就是str1从start1+1开始匹配,str2从start2+2开始匹配,如果不匹配 则考虑'*'当做2倍考虑,也就是str2从start1+1开始匹配,str2从start2+2开始匹配,如果不匹配 ... 直到匹配或者某一个越界为止 """ if processMatchStr(strList1, strList2, start1, start2 + 2): return True start1 += 1 return processMatchStr(strList1, strList2, start1, start2 + 2) # dp(横轴:exp(范围[0, len(exp)]), 纵轴: str(范围[0, len(str)]))) # 由原题意可知,dp[i][j]与dp[i + 1][j + 1]、dp[i][j + 2]、dp[i++][j + 2]有关,因此,需要知道dp表中最后两列和最后一排的数,才能推出整张表 # 最后一列的意义:j=len(exp),已经没有字符可用了,此时str仍有待匹配项,所以全部为False(除了i=len(str),j=len(exp),此时匹配项和待匹配项整好全部耗尽,此位置为True) # 倒数第二列的意义:此时exp仅剩最后一个字符,而待匹配项还有好多,全部为False。只有str[len(str)-1]==exp[len(exp)-1],或者exp[len(exp)-1]='.'时该位置为True,其他位置全为False # 最后一排的意义:此时待匹配项str已经没有字符需要被匹配,而匹配项还有好多。只有匹配项满足'a*a*a*...',这样才会为True,否则是False # 当推出普遍位置后,发现basicase不够,需要还原到原题意,把欠缺的位置补上 def isMatchDPMatrix(str1, str2): if str1 == "" or str2 == "": return False strList1, strList2 = [], [] for i in str1: strList1.append(i) for i in str2: strList2.append(i) if not isValidStr(str1, str2): return False dpMatrix = initDPMap(strList1, strList2) for i in range(len(strList1) - 1, -1, -1): for j in range(len(strList2) - 2, -1, -1): if strList2[j + 1] != '*': dpMatrix[i][j] = (strList1[i] == strList2[j] or strList2[j] == '.') and dpMatrix[i + 1][j + 1] else: si = i while si != len(strList1) and (strList1[si] == strList2[j] or strList2[j] == '.'): if dpMatrix[si][j + 2]: dpMatrix[i][j] = 1 break si += 1 if dpMatrix[i][j] != 1: dpMatrix[i][j] = dpMatrix[si][j + 2] return dpMatrix[0][0] def initDPMap(strList1, strList2): strLen1, strLen2 = len(strList1), len(strList2) dpMatrix = np.zeros((strLen1 + 1, strLen2 + 1)) dpMatrix[strLen1][strLen2] = 1 for j in range(strLen2 - 2, -1, -2): # 最后一排要满足'a*a*a*...'这种情况,才会为True,否则为False if strList2[j] != '*' and strList2[j + 1] == '*': dpMatrix[strLen1][j] = 1 else: break if strLen1 > 0 and strLen2 > 0: if strList2[strLen2 - 1] == '.' or strList1[strLen1 - 1] == strList2[strLen1 - 1]: # 此时str1还剩最后一个待匹配项,str2也仅剩最后一个匹配项。 dpMatrix[strLen1 - 1][strLen2 - 1] = 1 return dpMatrix