函数练习

1.把一个字典扁平化,源字典为{'a':{'b':1,'c':2}, 'd':{'e':3,'f':{'g':4}}} 。

 

上面字典的扁平化可以转化为下面的字典:{“a.c”:2,"d.e":3,"d.f.g":4,"a.b":1}

source = {'a':{'b':1,'c':2}, 'd':{'e':3,'f':{'g':4}}}
target = {}

#recursion
def flatmap(src,prefix = ""):
    for k,v in src.items():
        if isinstance(v,(list,tuple,set,dict)):
            flatmap(v,prefix=prefix+k+".")#递归调用
        else:
            target[prefix+k]=v
            
flatmap(source)
print(target)

结果为:

{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}

像一般这样的函数都会生成一个新的字典,上面的代码借用了外部的变量,破坏了函数的封装,因此可以对上面的函数稍微改造下,dest字典可以由内部来创建,当然也可以外部提供。

source = {'a':{'b':1,'c':2}, 'd':{'e':3,'f':{'g':4}}}

#recursion
def flatmap(src,dest=None,prefix = ""):
    if dest ==None:
        dest = {}
    for k,v in src.items():
        if isinstance(v,(list,tuple,set,dict)):
            flatmap(v,dest,prefix=prefix+k+".")#递归调用
        else:
            dest[prefix+k]=v
    return dest

print(flatmap(source))

结果为:

{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}

上面的函数有一个缺点,那就是将内部的字典暴露给了外部,能否函数提供一个参数源字典,返回一个新的扁平化字典?递归的时候要把目标字典的引用传递多层,这个时候应该怎么处理?

source = {'a':{'b':1,'c':2}, 'd':{'e':3,'f':{'g':4}}}

#recursion
def flatmap(src):
    def _flatmap(src,dest=None,prefix=""):
        for k,v in src.items():
            key = prefix+k
            if isinstance(v,(list,tuple,set,dict)):
                _flatmap(v,dest,key+".")#递归调用
            else:
                dest[key]=v
        
    dest = {}
    _flatmap(src,dest)
    return dest

print(flatmap(source))

结果为:
{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}

2.实现base64编码,要求自己实现算法,不用库。

 

 将输入每3个字节断开,拿出一个3个字节,每6个bit断开成4段。2**6=64,因此有了base64的编码表。每一段当做一个8bit看它的值,这个值就是base64编码表的索引值,找到对应字符。再取出3个字节,同样处理,直到最后。

举例:

abc对应的ASCII码为:0x61 0x62 0x63

01100001 01100010 01100011#abc

011000 010110 001001 100011

00011000 00010110 00001001 00100011 #每6位补齐为8位

24 22 9 35

末尾的处理?

  1. 正好3个字节,处理方式同上。
  2. 剩1个字节或2个字节,用0补满3个字节。
  3. 补0的字节用=表示。
# 自己实现对一段字符串进行base64编码

alphabet = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwsyz0123456789+/"

teststr = "abcd"
tsetstr ="Manma"

def base64(src):
    ret = bytearray()
    length = len(src)
    #用r记录补0的个数
    r = 0
    for offset in range(0,length,3):
        if offset+3<=length:
            triple = src[offset:offset+3]
        else:
            triple = src[offset:]
            r = 3-len(triple)
            triple = triple +"\x00"*r#补几个0
            
        #print(triple,r)
        #将3个字节看成一个整体转成字节bytes,大端模式
        #abc=>0x616263
        b = int.from_bytes(triple.encode(),"big")#小端模式为"little"
        print(hex(b))
        
        
        #01100001 01100010 01100011 #abc
        #011000 010110 001001 100011 #每6位断开
        
        for i in range(18,-1,-6):
            if i==18:
                index = b>>i
            else:
                index = b>>i &0x3F #0b0011 1111
            ret.append(alphabet[index])#得到base64编码的列表
        #策略是不管是不是补0,都补满,只有最后一次可能出现补0的
        #在最后替换掉就是了,代码清晰,而且替换至多2次
        #在上一个循环中判断r!=0,效率可能会更高些
        
        for i in range(1,r+1):#1到r,补几个0替换几个=
            ret[-i]=0x3D
    return ret

print(base64(tsetstr))

结果为:
0x4d616e
0x6d6100
bytearray(b'TWFubWE=')
#base64实现 
import base64
print(base64.b64encode(teststr.encode()))

结果为:
b'TWFubWE='

练习3:求两个字符串的最长公共子串

思考:

s1 = "abcdefg"

s2 = "defabcd"

方法一:矩阵算法

让s2的每一个元素,去分别和s1的每一个元素比较,相同为1 ,不同为0,有下面的矩阵。

 

 上面都是s1的索引。

看与斜对角平行的线,这个线是穿过1的,那么最长的就是最长子串。

print(s1[3:3+3])

print(s1[0:0+4])最长

 

矩阵求法还需要一个字符扫描最长子串的过程,扫描的过程就是len(s1)len(s2)次,O(nm).

有办法一遍循环就找出最长的子串嘛?

0001000第一行,索引为3,0。

第二行的时候如果4,1是1,就判断3,0是否为1,为1就把3,0加1。

第二行的时候如果5,2是1,就判断4,1是否为1,是1就加1,再就判断3,0是否为1,为1就把3,0加1 。

 

 上面的方法是个递归问题,不好。

最后在矩阵中找到最大的元素,从它开始就能写出最长的子串了。

但是这个不好算,因为是逆推的,改为顺推。

 

 顺推的意思,就是如果找到一个就看前一个的数字是几,然后在它的基础上加1。

s1 = "abcdefg"
s2 = "defabcd"
s2 = "defabcdoabcdeftw"
s3 = "1234a"
s4 = "5678"
s5 = "abcdd"

def findit(str1,str2):
    matrix = []
    #从x轴或者y轴取都可以,选择x轴,xmax和xindex
    xmax = 0
    xindex = 0
    for i,x in enumerate(str2):
        matrix.append([])
        for j,y in enumerate(str1):
            if x!=y:#若两个字符不相等
                matrix[i].append(0)
            else:
                if i==0 or j ==0:#两个字符相等,有字符在边上的
                    matrix[i].append(1)
                else:#不在边上
                    matrix[i].append(matrix[i-1][j-1]+1)

                if matrix[i][j]>xmax:#判断当前加入的值和记录的最大值比较
                    xmax = matrix[i][j]#记录最大值,用于下次比较
                    xindex = j#记录当前值的x轴偏移量,和str1[xindex+1-xmax:xindex+1匹配]
                    xindex+=1#只是为了计算的需求才+1,和str1[xindex-xmax:xindex]匹配
                
    #return str1[xindex+1-xmax:xindex+1]
    return str1[xindex - xmax:xindex]


print(findit(s1,s2)) 
print(findit(s1,s3))               
print(findit(s1,s4))             
print(findit(s1,s5))  


s1 = " abcdefg "
s2 = "304abcdd"
print(findit(s1,s5))


结果为:

abcdef
a

abcd
abcd

方法二:

可不可以这样思考?

字符串都是连续的字符,所以才有了下面的思路。

思路一:

第一轮

从s1中依次取1个字符,在s2中查找,看是否能够找到子串。

如果没有一个字符在s2中找到,说明就没有公共子串,直接退出。如果找到了至少一个公共子串,则很有可能还有更长的公共子串,可以进入下一轮。

第二轮

然后从s1中取连续的2个字符,在s2中查找,看看能够找到公共的子串。如果没有找到,说明最大公共子串就是上一轮的随便的哪一个就行了。如果找到至少一个,则说明公共子串可能还可以再长一些。可以进入下一轮。

改进,其实只要找到第一轮的公共子串的索引,最长公共子串也就是从它开始的,所以以后的轮次都从这些索引位置开始,可以减少比较的次数。

思路二:

既然是求最大子串,我先看s1全长作为子串。

在s2中搜索,是否返回正常的index, 正常就找到了最长的子串。

没有找到,把s1按照length-1取多个子串。

在s2中搜索,是否能返回正常的index。

注意:

不要一次把s1的所有子串生成,用不了,也不要从最短开始,因为题目要最长的。

但是也要注意,万一他们的公共子串就只有一个字符,或者很少字符的,思路一就会占优势。

s1 = "abcdefg"
s2 = "defaabcdoabcdeftw"
s3 = "1234a"

def findit(str1,str2):
    count = 0#看看效率,计数
    length = len(str1)
    
    for sublen in range(length,0,-1):
        for start in range(0,length - sublen +1):
            substr = str1[start:start+sublen]
            count+=1
            if str2.find(substr)>-1:#found
                print("count={},substrlen={}".format(count,sublen))
                return substr
print(findit(s1,s2))
print(findit(s1,s3))

结果为:

count=2,substrlen=6
abcdef
count=22,substrlen=1
a

 

posted on 2019-10-21 23:30  xpc199151  阅读(158)  评论(0编辑  收藏  举报

导航