zoukankan      html  css  js  c++  java
  • 递归+HashMap套路(可以对付可降解问题)

    115. 不同的子序列

    给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。

    一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

    题目数据保证答案符合 32 位带符号整数范围。

    示例 1:

    输入:S = "rabbbit", T = "rabbit"

    输出:3

    解释:

    如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。

    rabbbitrabbbitrabbbit 就是三个bbb,中不取哪个b,有三种情况。

    个人考虑是用递归:

    匹配了一个,那么就是S,T从这开始后子串再做比较(递归),当然因为不是只找出一个解,而是求一共有多少,所以这个不去匹配,从S的下一个位置开始,继续找匹配的代码也要写。

    然后就是递归一旦开多条分支,就会组合爆炸(实际一条分支是不会的,问题是一条分支,开递归干嘛,除非是教程里的阶乘,这个本来就不需要递归)。为了不重复计算,采用了S剩下几个,T剩下几个字母,然后有多少解,除了第一次,算完后,这个放在map中保存(S剩余字母数*1000+T剩余字母数,作为key,值是多少解),以后遇到同样情况,直接就用map里的值,这样大大提升效率,因为他里面测试的S是有几百个字符的,组合可以到几千万种,用了map,实际就记录了200多个,速度也快多了。不过即便如此,个人的程序执行速度是仅仅击败了5.59%的用户(500ms),从CSDN里找了一个高手的程序,放在LeetCode测试,果然他的只用了20多ms。哦,太惊人了。

    当然个人并没有学习高手的逻辑,估计是非常贴合该问题的。作为一般程序员,完全贴合问题的数学模型是有难度的,用递归+map(防止组合爆炸),可以解决许多问题(可降解问题),一般x(N)转成x(N-1)或x(N-m)问题,总是朝着解决的方向前进,而只要有个递归式,以及收尾(到某一步不再递归),另外进入函数时判断map中是否存在,退出函数时,结果记入map。就这样一套,应该能解决一大类问题,包括动态规划。。。

    class Solution(object):
        def numDistinct(self, s, t):
            map = {}
            return self.num_dist(s,t,map)
        def num_dist(self,s,t,map):
            c = 0
            for i in range(len(s)-len(t)+1):
                if s[i] == t[0]:
                    if len(t) == 1:
                        c += 1
                    else:
                        v = map.get((len(s)-i-1)*1000+len(t)-1)
                        c = c+v if v != None else c+ self.num_dist(s[i+1:],t[1:],map)
            map.update({len(s)*1000+len(t):c})
            return c

     又比如:LeetCode410

    410. 分割数组的最大值

    给定一个非负整数数组和一个整数 m,你需要将这个数组分成 个非空的连续子数组。设计一个算法使得这 个子数组各自和的最大值最小。

    注意:
    数组长度 满足以下条件:

    • 1 ≤ n ≤ 1000
    • 1 ≤ m ≤ min(50, n)

    示例:

    输入:
    nums = [7,2,5,10,8]
    m = 2
    
    输出:
    18
    
    解释:
    一共有四种方法将nums分割为2个子数组。
    其中最好的方式是将其分为[7,2,5] 和 [10,8],
    因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

    思路:
    其实数组是没有排序的,一开始以为是排序数组,那么就从末段开始查找,很少几个数就能超过平均值,
    找到后,比如上边例子:8没有超过一半,再加上10就超过一半了,这时:
    A1[7,2]分一组,A2[5,10,8] 这里面取大
    B1[7,2,5]分一组,B2[10,8] 这里面取大
    C1[7,2,510]分一组,C2[8] 这里面取大
    然后这三行中取小,当然这里m=2,实际m可以等于50,这个时候,前面的部分调用split_x(nums,i+1,m-1,mapx)
    就是前面部分再分成49组,这样的递归,为了防止组合爆发,加个map就行了,另外也采用剪枝
    继续用上边例子:
    对于某一搜索的结果:min(max(A1,A2),max(B1,B2),max(C1,C2))
    A1<=B1<=C1,A2>B2>C2
    当B2>C1的时候,实际B1就不用求了,A1更不用谈了
    时间也从原来不知道多少秒(不加map太可怕了)变成了15ms(一共有27个测试)
    if True:
        class Solution410(object):
            def splitArray(self, nums, m):
                mapx = {}
                v = self.split_x(nums,len(nums),m,mapx)
                print(len(mapx))
                return v
            def split_x(self,nums,last_ptr,m,mapx):
                v = mapx.get(last_ptr*100+m)
                if v != None:return v
                
                if m == 1:
                    v = sum(nums[:last_ptr])
                else:
                    sum_last = 0
                    sum_share = sum(nums[:last_ptr])//m
                    for i in range(last_ptr-1,-1,-1):
                        sum_last += nums[i]
                        if sum_last > sum_share:
                            v2 = self.split_x(nums,i+1,m-1,mapx)
                            if sum_last > v2:
                                v = v2
                            else:
                                v1 = self.split_x(nums,i,m-1,mapx)
                                sum_left = sum_last+nums[i-1]
                                v = min(max(v1,sum_last),v2) if sum_left > v2 else min(max(self.split_x(nums,i-1,m-1,mapx),sum_left),max(v1,sum_last),v2)
                            break
                mapx.update({last_ptr*100+m:v})
                return v
    
        print(Solution410().splitArray([7,2,5,10,8],2))
        print(Solution410().splitArray([499,498,497,496,495,494,493,492,491,490,489,488,487,486,485,484,483,482,481,480,479,478,477,476,475,474,473,472,471,470,469,468,467,466,465,464,463,462,461,460,459,458,457,456,455,454,453,452,451,450,449,448,447,446,445,444,443,442,441,440,439,438,437,436,435,434,433,432,431,430,429,428,427,426,425,424,423,422,421,420,419,418,417,416,415,414,413,412,411,410,409,408,407,406,405,404,403,402,401,400,399,398,397,396,395,394,393,392,391,390,389,388,387,386,385,384,383,382,381,380,379,378,377,376,375,374,373,372,371,370,369,368,367,366,365,364,363,362,361,360,359,358,357,356,355,354,353,352,351,350,349,348,347,346,345,344,343,342,341,340,339,338,337,336,335,334,333,332,331,330,329,328,327,326,325,324,323,322,321,320,319,318,317,316,315,314,313,312,311,310,309,308,307,306,305,304,303,302,301,300,299,298,297,296,295,294,293,292,291,290,289,288,287,286,285,284,283,282,281,280,279,278,277,276,275,274,273,272,271,270,269,268,267,266,265,264,263,262,261,260,259,258,257,256,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0],50))
  • 相关阅读:
    plink:将bed文件转化为ped,map文件
    linux下查找某文件关键字(grep 函数)
    genetic model
    linux下设置默认路径
    vi怎么查找关键字
    有意思的undefined columns selected,源于read.table和read.csv
    练习2-2
    练习2-1
    排序算法之堆排序
    Java实现二叉树先序,中序,后序,层次遍历
  • 原文地址:https://www.cnblogs.com/nocomment/p/13274241.html
Copyright © 2011-2022 走看看