Python程序员面试分类真题15_第1页
Python程序员面试分类真题15_第2页
Python程序员面试分类真题15_第3页
Python程序员面试分类真题15_第4页
Python程序员面试分类真题15_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Python程序员面试分类真题15(总分:100.00,做题时间:90分钟)面试题(总题数:5,分数:100.00)1.

编辑距离又称Levenshtein距离,是指两个字符串之间由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符、插入一个字符、删除一个字符。请设计并实现一个算法来计算两个字符串的编辑距离,并计算其复杂度。在某些应用场景下,替换操作的代价比较高,假设替换操作的代价是插入和删除的两倍,算法该如何调整?

(分数:20.00)__________________________________________________________________________________________

正确答案:(本题可以使用动态规划的方法来解决,具体思路如下:

给定字符串s1,s2,首先定义一个函数D(i,j)(0≤i≤strlen(s1),0≤j≤strlen(s2)),用来表示第一个字符串s1长度为i的子串与第二个字符串s2长度为j的子串的编辑距离。从s1变到s2可以通过如下三种操作完成:

(1)添加操作。假设已经计算出D(i,j-1)的值(s1[0…i]与s2[0…j-1]的编辑距离),则D(i,j)=D(i,j-1)+1(s1长度为i的字串后面添加s2[j]即可)。

(2)删除操作。假设已经计算出D(i-1,j)的值(s1[0…i-1]到s2[0…j]的编辑距离),则D(i,j)=D(i-1,j)+1(s1长度为i的字串删除最后的字符s1[j]即可)。

(3)替换操作。假设已经计算出D(i-1,j-1)的值(s1[0…i-1]与s2[0…j-1]的编辑距离),如果s1[i]=s2[j],那么D(i,j)=D(i-1,j-1),如果s1[i]!=s2[j],那么D(i,j)=D(i-1,j-1)+1(替换s1[i]为s2[j],或替换s2[j]为s1[i])。

此外,D(0,j)=j且D(i,0)=i(一个字符串与空字符串的编辑距离为这个字符串的长度)。

由此可以得出如下实现方式:对于给定的字符串s1、s2,定义一个二维数组D,则有以下几种可能性。

(1)如果i==0,那么D[i,j]=j(0≤j≤strlen(s2))。

(2)如果j==0,那么D[i,j]=i(0≤i≤strlen(s1))。

(3)如果i>0且j>0,

(a)如果s1[i]==s2[j],那么D(i,j)=min{edit(i-1,j)+1,edit(i,j-1)+1,edit(i-1,j-1)}。

(b)如果s1[i]!=s2[j],那么D(i,j)=min{edit(i-1,j)+1,edit(i,j-1)+1,edit(i-1,j-1)+1}。

通过以上分析可以发现,对于第一个问题可以直接采用上述的方法来解决。对于第二个问题,由于替换操作是插入或删除操作的两倍,只需要修改如下条件即可:

如果s1[i]!=s2[j],那么D(i,j)=min{edit(i-1,j)+1,edit(i,j-1)+1,edit(i-1,j-1)+2}。

根据上述分析,给出实现代码如下:

classEditDistance:

defmins(self,a,b,c):

tmp=aifa<belseb

returntmpiftmp<celsec

#参数replaceWight用来表示替换操作与插入删除操作相比的倍数

defedit(self,s1,s2,replaceWight):

#两个空串的编辑距离为0

ifs1==Noneands2==None:

return0

#如果s1为空串,那么编辑距离为s2的长度

ifs1==None:

returnlen(s2)

ifs2==None:

returnlen(s1)

len1=len(s1)

len2=len(s2)

#申请二维数组来存储中间的计算结果

D=[([None]*(len2+1))foriinrange(len1+1)]

i=0

whilei<len1+1:

D[i][0]=i

i+=1

i=0

whilei<len2+1:

D[0][i]=i

i+=1

i=1

whilei<len1+1:

j=1

whilej<len2+1:

iflist(s1)[i-1]==list(s2)[j-1]:

D[i][j]=self.mins(D[i-1][j]+1,D[i][j-1]+1,\D[i-1][j-1])

else:

D[il[j]=min(D[i-1][j]+1,D[i][j-1]+1,D[i-1][j-1]\+replaceWight)

j+=1

i+=1

print"--------------------"

i=0

whilei<len1+1:

j=0

whilej<len2+1:

printD[i][j],

j+=1

print'\n'

i+=1

print"--------------------"

dis=D[len1][len2]

returndis

if__name__=="__main__":

s1="bciln"

s2="fciling"

ed=EditDistance()

print”第一问:”

print”编辑距离:”+str(ed.edit(s1,s2,1))

print”第二问:”

print”编辑距离:”+str(ed.edit(s1,s2,2))

程序的运行结果为:

第一问:

-----------------------------------------

01234567

11234567

22123456

33212345

44321234

55432223

-----------------------------------------

编辑距离:3

第二问:

-----------------------------------------

01234567

12345678

23234567

34323456

45432345

56543434

-----------------------------------------

编辑距离:4

算法性能分析:

这种方法的时间复杂度与空间复杂度都为O(m*n)(其中,m、n分别为两个字符串的长度)。)解析:[考点]如何求字符串的编辑距离2.

寻找一条从左上角(arr[0][0])到右下角(arr[m-1][n-1])的路线,使得沿途经过的数组中的整数的和最小。

(分数:20.00)__________________________________________________________________________________________

正确答案:(对于这道题,可以从右下角开始倒着来分析这个问题:最后一步到达arr[m-1][n-1]只有两条路:通过arr[m-2][n-1]到达或通过arr[m-1][n-2]到达,假设从arr[0][0]到arr[m-2][n-1]沿途数组最小值为f(m-2,n-1),到arr[m-1][n-2]沿途数组最小值为f(m-1,n-2)。因此,最后一步选择的路线为min{f(m-2,n-1),f(m-1,n-2)}。同理,选择到arr[m-2][n-1]或arr[m-1][n-2]的路径可以采用同样的方式来确定。

由此可以推广到一般的情况。假设到arr[i-1][j]与arr[i][j-1]的最短路径的和为f(i-1,j)和f(i,j-1),那么到达arr[i][j]的路径上的所有数字和的最小值为:f(i,j)=min{f(i-1,j),f(i,j-1)}+arr[i][j]。

方法一:递归法

根据这个递归公式可知,可以采用递归的方法来实现,递归的结束条件为遍历到arr[0][0]。在求解的过程中还需要考虑另外一种特殊情况:遍历到arr[i][j](当i=0或j=0)的时候只能沿着一条固定的路径倒着往回走直到arr[0][0]。根据这个递归公式与递归结束条件可以给出实现代码如下:

defgetMinPath(arr,i,j):

#倒着走到了第一个结点,递归结束

ifi==0andj==0:

returnarr[i][j]

#选取两条可能路径上的最小值

elifi>0andj>0:

returnarr[i][j]+\

min(getMinPath(arr,i-1,j),getMinPath(arr,i,j-1))

#下面两个条件只可选择一个

elifi>0andj==0:

returnarr[i][j]+getMinPath(arr,i-1,j)

#j>0&&i==0

else:

returnarr[i][j]+getMinPath(arr,i,j-1)

defgetMinPath2(arr):

ifarr==Noneorlen(arr)==0:

return0

returngetMinPath(arr,len(arr)-1,len(arr[0])-1)

if__name__=="__main__":

arr=[[1,4,3],[8,7,5],[2,1,5]]

printgetMinPath2(arr)

程序的运行结果为:

17

这种方法虽然能得到题目想要的结果,但是效率太低,主要是因为里面有大量的重复计算过程,比如在计算f(i-1,j)与f(j-1,i)的过程中都会计算f(i-1,j-1)。如果把第一次计算得到的f(i-1,j-1)缓存起来,那么就不需要额外的计算了,而这也是典型的动态规划的思路,下面重点介绍动态规划方法。

方法二:动态规划法

动态规划法其实也是一种空间换时间的算法,通过缓存计算中间值,从而减少重复计算的次数,从而提高算法的效率。方法一从arr[m-1][n-1]开始逆向通过递归来求解,而动态规划要求正向求解,以便利用前面计算出来的结果。

对于本题而言,显然f(i,0)=arr[0][0]+…+arr[i][0],f[0,j]=arr[0][0]+…+arr[0][j]。根据递推公式:f(i,j)=min{f(i-1,j),f(i,j-1)}+arr[i][j],从i=1,j=1开始顺序遍历二维数组,可以在遍历的过程中求出所有的f(i,j)的值,同时把求出的值保存到另外一个二维数组中以供后续使用。当然在遍历的过程中可以确定这个最小值对应的路线,在这种方法中,除了求出最小值以外顺便还打印出了最小值的路线,实现代码如下:

defgetMinPath(arr):

ifarr==Noneorlen(arr)==0:

return0

row=len(arr)

col=len(arr[0])

#用来保存计算的中间值

cache=[([None]*col)foriinrange(row)]

cache[0][0]=arr[0][0]

i=1

whilei<col:

cache[0][i]=cache[0][i-1]+arr[0][i]

i+=1

j=1

whilej<row:

cache[j][0]=cache[j-1][0]+arr[j][0]

j+=1

#在遍历二维数组的过程中不断把计算结果保存到cache中

print"路径:",

i=1

whilei<row:

j=1

whilej<col:

#可以确定选择的路线为arr[i][j-1]

ifcache[i-1][j]>cache[i][j-1]:

cache[i][j]=cache[i][j-1]+arr[i][j]

print"["+str(i)+","+str(j-1)+"]",

#可以确定选择的路线为arr[i-1][j]

else:

cache[i][j]=cache[i-1][j]+arr[i][j]

print"["+str(i-1)+","+str(j)+"]",

j+=1

i+=1

print("["+str(row-1)+","+str(col-1)+"]")

return"最小值为:"+str(cache[row-1][col-1])

if__name__=="__main__":

arr=[[1,4,3],[8,7,5],[2,1,5]]

printgetMinPath(arr)

程序的运行结果为:

路径:[0,1][0,2][2,0][2,1][2,2]

最小值为:17

算法性能分析:

这种方法对二维数组进行了一次遍历,因此其时间复杂度为O(m*n)。此外由于这种方法同样申请了一个二维数组来保存中间结果,因此其空间复杂度也为O(m*n)。)解析:[考点]如何在二维数组中寻找最短路线3.

编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。但是要保证汉字不被截半个,例如"人ABC"4,应该截为"人AB",输入"人ABC们DEF",6,应该输出为"人ABC"而不是"人ABC+们的半个"。

(分数:20.00)__________________________________________________________________________________________

正确答案:(在Python2.7语言中,在开头声明encoding为utf8,一个中文占3个字节。本题先将中文转成unicode编码,便于识别是否是中文,再根据字符长度进行截取。根据这个思路,可以采用如下代码来满足题目的要求:

#判断字符c是否是中文字符,如果是返回True

defisChinese(c):

returnTrueifc>=u'\u4e00'andc<=u'\u9fa5'elseFalse

deftruncateStr(strs,lens):

if(strs==Noneorstrs==""orlens==0):

return""

#chrArr=list(strs)

chrArr=strs

sb=""

count=0#用来记录当前截取字符的长度

forccinchrArr:

if(count<lens):

#printsb,count

if(isChinese(cc)):

#如果要求截取予串的长度只差1个或者2个字符,但是接下来的字符是中文,

#那么截取结果子串中不保存这个中文字符

ifcount+1<=lensandcount+3>lens:

returnsb

count=count+3

sb=sb+cc

else:

count=count+1

sb=sb+cc

else:

break

returnsb

if__name__=="__main__":

sb="人ABC们DEF"

sb_unicode=unicode(sb,'utf8')#转成unicode编码

printtruncateStr(sb_unicode,6)

程序的运行结果为:

人ABC)解析:[考点]如何截取包含中文的字符串4.

编写一个函数,根据两个文件的绝对路径算出其相对路径。例如

a="/qihoo/app/a/b/c/d/new.c",b="/qihoo/app/1/2/test.c",那么b相对于a的相对路径是"../../../../1/2/teSt.c"

(分数:20.00)__________________________________________________________________________________________

正确答案:(首先找到两个字符串相同的路径(/aihoo/app),然后处理不同的目录结构(a="/a/b/c/d/new.c",b="/1/2/test.c")。处理方法为:对于a中的每一个目录结构,在b前面加“../”,对于本题而言,除了相同的目录前缀外,a还有四级目录a/b/c/d,因此只需要在b="/1/2/test.c"前面增加四个"../"得到的"../../"/../1/2/test.c"就是b相对a的路径。实现代码如下:

defgetRelativePath(path1,path2):

ifpath1==Noneorpath2==None:

print"参数不合法\n"

return

relativePath=""

#用来指向两个路径中不同目录的起始路径

diff1=0

diff2=0

i=0

j=0

len1=len(path1)

len2=len(path2)

whilei<len1andj<len2:

#如果目录相同,那么往后遍历

iflist(path1)[i]==list(path2)[j]:

iflist(path1)[i]=='/':

diff1=i

diff2=j

i+=1

j+=1

else:

#不同的目录

#把path1非公共部分的目录转换为"/

diff1+=1#跳过目录分隔符/

whilediff1<len1:

#碰到下一级目录

iflist(path1)[diff1]=='/':

relativePath+="../"

diff1+=1

#把path2的非公共部分的路径加到后面

diff2+=1

relativePath+=path2[diff2:]

break

returnrelativePath

if__name__=="__main__":

path1="/qihoo/app/a/b/c/d/new.c"

path2="/qihoo/app/1/2/test.c"

printgetRelativePath(path1,path2)

程序的运行结果为:

../../../../1/2/test.c

算法性能分析:

这种方法的时间复杂度与空间复杂度都为O(max(m,n))(其中,m、n分别为两个路径的长度)。)解析:[考点]如何求相对路径5.

给定一个词典和两个长度相同的“开始”和“目标”的单词。找到从“开始”到“目标”最小链的长度。如果它存在,那么这条链中的相邻单词只有一个字符不同,而链中的每个单词都是有效的单词,即它存在于词典中。可以假设词典中存在“目标”字,所有词典词的长度相同。

例如:

给定一个单词词典为:[pooN,pbcc,zamc,poIc,pbca,pbIc,poIN]

start=TooN

target=pbca

输出结果为:7

因为:TooN(start)-pooN-poIN-poIc-pbIc-pbcc-pbca(target).

(分数:20.00)__________________________________________________________________________________________

正确答案:(本题主要的解决方法为:使用BFS的方式从给定的字符串开始遍历所有相邻(两个单词只有一个不同的字符)的单词,直到遍历找到目标单词或者遍历完所有的单词为止。实现代码如下:

fromcollectionsimportdeque

#用来存储单词链的队列

classQItem:

def_init__(self,word,lens):

self.word=word

self.lens=lens

#判断两个字符串是否只有一个不同的字

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论