数据结构与算法 ——1 变位词判断
变位词是指两个词之间存在组成字母的重新排列顺序,比如acd和bac。
我们可以写一个bool函数,以需要判断是否是变位词的两个词作为参数,返回这两个词是否变位词,返回True则两个词是变位词,返回False就不是变位词。
思路1:
将词1中的字符逐个到词2中检查是否存在,存在就打勾标记,这样可以防止重复检查。如果词1中的每个字符都能在词2中找到,则两个词是变位词,但只要一个字符找不到这两个词就不是变位词。
由于字符串是不可变类型,我们需要先将其复制到列表中。实现打勾标记:将词1在词2中找到的对应字符设为None。
在 Python 中,字符串本身就是可迭代的序列类型,可以直接通过索引(如 s1[order1])或循环来访问其中的字符。s2 被转换成列表 ls,主要是为了标记已经匹配过的字符(通过将已匹配的字符设为 None),避免重复匹配同一个字符。
代码实现:
def pd(s1,s2):
ls=list(s2)#把s2转换成list类型,复制到一个列表中
order1=0
s_ok=True
'''循环s1当中的每一个字符'''
while order1<len(s1) and s_ok: #这里是当字符串1的长度大于order1(order随循环次数增加递增)且s_ok为True,两者同时满足时继续循环
order2=0
found=False #先给found参数赋值bool类型的False,后续匹配到字符之后修改赋值为True
'''循环s2中的每个字符,把s1当中的字符在s2中进行对比'''
while order2<len(ls) and not found: #此循环的条件为这里是当字符串1的长度大于order1(order随循环次数增加递增)且True
if s1[order1]==ls[order2]: #如果对比成功
found=True #找到了,赋值为true
else: #如果没有跟s1中此字符匹配成功,就去匹配下一个ls中的字符
order2=order1+1 #直到不满足第二层while循环的条件
if found: #其实就是if true,如果找到就打勾(将找到的这个字符赋值为空,后续不再参加匹配),order2就是找到的这个字符的下标位置
ls[order2]=None
else:#只要有一个字符没找到就失败
s_ok=False #这个为false,那第五行直接退出来
order1=order1+1 #使用下一个s1字符去进行匹配
return s_ok #如果找到了就返回true,否则false
result=pd('abcd','bacd')
print(result)
词中包含的字符个数n,主要部分在于两重循环,外层遍历s1每个字符,将内层循环执行n次。内层循环在s2中查找字符,每个字符的对比次数,分别是1、2、3……n中的一个,并且每个字符的对比次数不一样。
那么总执行次数为1+2+……+n,时间复杂度o(n^2)
思路2:排序比较
将两个字符串按照字母顺序排好序,再逐个字符进行对比是否相同,如果相同就是变位词,有任何不同就不是。
代码实现:
def pd(s1,s2):
'''将两个词分别转换为列表'''
ls1=list(s1)
ls2=list(s2)
'''分别对两个列表进行排序'''
ls1.sort()
ls2.sort()
pos=0
match=True
while pos<len(s1) and match:#这里是当字符串1的长度大于pos(pos随循环次数增加递增)且match为True,两者同时满足时继续循环
if ls1[pos]==ls2[pos]:
pos=pos+1#可以匹配就检查下一个
else:
match=False#匹配不上就直接失败
return match #返回True或者False,也就是对应匹配成功或者匹配失败
print(pd('abcd','badc'))
两个sort排序算法采用不同的解决方法,运行时间数量级差不多是o(n^2)\o(nlogn),大于循环的o(n),则本算法时间主导的步骤是排序步骤。
本算法的运行时间数量级就等于排序过程的数量级o(n log n)。
思路3:
穷尽所有可能组合,也就是将s1中出现的字符进行全排列,再查看s2是否出现在全排列中。
但是产生s1所有字符的全排列,结合组合数学结论,如果n个字符进行全排列,其所有的字符串个数为n!。而n!的增长速度超过2^n,太复杂不是一个好算法
思路4:
对比两个词中每个字母出现的次数,如果26个字母出现的次数相同就是变位词。
我们可以为每个词设置一个26位的计数器,先检查每个词,在计数器中设定好每个字母出现的次数,计数完成后,进入比较阶段,看两个字符串的计数器是否相同,相同就是变位词。
计数比较有3个循环迭代但是并不嵌套,所以不用像嵌套那样需要相乘,只把几次循环的次数相加就可以了。第三个循环的次数是26。
代码实现:
def pd(s1,s2):
c1=[0]*26#计数器,26长度的列表
c2=[0]*26
for i in range(len(s1)):#对s1进行计数,列表的下表是从0-25整数,词字母是从a-z,
pos=ord(s1[i])-ord('a')#ord函数是用于返回一个字符的unicode编码,从目标字符的编码减去最开头字母a的编码就可以变成o-25的一个数
c1[pos]=c1[pos]+1#累加进计数器
for i in range(len(s2)):
pos=ord(s2[i])-ord('a')#对s2
c2[pos]=c2[pos]+1
j=0
s_ok=True
while j<26 and s_ok:#对计数器进行一位一位的比较,26位固定个数
if c1[j]==c2[j]:#判断两个计数器每一个位置的数是否相同
j=j+1#相同就比较下一位
else:#一旦不一样直接返回false
s_ok=False
return s_ok
print(pd('abcd','bacd'))
总操作次数是T(n)=2n+26,其数量级为o(n),这是线性数量级,最优的算法。
但是本算法依赖与长度为26的计数器列表,来保存字符技术,相比较而言需要更多的存储空间,需要存在时间和空间之间的取舍。
可以用字典进行改造!--代码
def pd(s1, s2):
# 使用字典代替列表作为计数器
count = {}
# 统计s1中每个字符的出现次数
for char in s1:
count[char] = count.get(char, 0) + 1
# 统计s2中每个字符的出现次数,并减去
for char in s2:
if char not in count:
return False
count[char] -= 1
# 检查所有计数是否为0
for val in count.values():
if val != 0:
return False
return True
print(pd('abcd', 'bacd'))
这几段代码最后的输出都为True。
更多推荐
所有评论(0)