③定义单词频率字典:
# 定义单词频率字典 word_dic = {}
④定义读取数据函数:
# 定义读取数据函数 def mostFrequentWords(filenames): inputFile = open(filenames, "r") # 循环进行处理 while True: # 按行读入 line = inputFile.readline() # 如果为EOF则结束读入 if not line: break # 如果是空行则忽略 if line == '\n': continue # 对非空行进行处理 wordlist = parse(line) # 使用字典保存频率 for temp in wordlist: if temp in word_dic.keys(): word_dic[temp] += 1 else: word_dic[temp] = 1 inputFile.close()
L3:获取输入流文件,对文章进行读入。
L5:通过循环对单词进行频率统计。
L7:获取输入流。
L9-L11:如果读入完成,则结束输入
L12-L14:如果是空行,则忽略
L16-L17:如果不是空行,则调用切分函数对单词进行切分并获得返回的结果列表
L20-L24:遍历结果列表中每个单词,如果能在字典中找到,则值加一;如果不能找到则以单词为键值,创建对应键值对。
⑤获取输入:
# 进行测试 # 首先读入对应的txt文件 mostFrequentWords('treasure.txt') mostFrequentWords('hyde.txt')
⑥进行排序统计:
# 定义结果列表,便于进行排序 res = [] # 将字典转为元组列表,便于排序 for temp in word_dic: tuple_temp = (word_dic[temp], temp) res.append(tuple_temp) # 进行排序 res.sort() res.reverse()
L2:定义空列表方便对单词频率进行保存
L5-L7:以值和键值构建元组,并将元组存入列表,便于排序
L10-L11:进行降序排序
⑦结果输出:
# 输出结果 print("Result:") for i in range(20): print("Frequency:"+str(res[i][0])+" Word: "+str(res[i][1]))
遍历结果列表中前20个元素,并进行输出。
代码测试:
①通过《The Strange Case of Dr. Jekyl and Mr. Hyde》和《Treasure Island》两篇文章分析Robert Louis Stevenson的写作风格:
②通过《War and Peace》分析Leo Tolstoy的写作风格:
4. 字符匹配
生活中很多例子都涉及到字符串的“接近”问题。比如我们搜索 Pytho,搜索引擎会回答“您是指 Python 吗?”又如,科学家检查一些核苷酸序列,想知道基因序列 AGTCGTC 和 TAGTCGT 有多匹配,或者说有多接近。
本题要探讨的一个大问题是:什么时候我们可以认为一个字符串与另一个字符串接近?或者说,我们什么时候可以将一个字符串视为另一个字符串的“邻居”?这里的“邻居”有三种可能的定义:
如果两个字符串除了在一个位置上不一样,其他位置都一样,如“abc”和“abe”;
如果可以通过交换一个字符串中的两个相邻字符来获得另外一个字符串, 如“abc”和“acb”;
如果从一个字符串中删除一个字符可以生成另一个字符串,如 “abc”和“abxc”。
任务和步骤:
(1)编写函数 offByOne(str1,str2),输入参数为两个非空的字符串 str1 和str2。仅当 str1 和 str2 具有相同的长度并且只在一个位置上不同时,返回True。例如:
(2)编写函数 offBySwap(str1,str2),输入参数为两个非空的字符串 str1 和str2。仅当 str1 不等于 str2,且可以将 str1 中任意两个相邻字符交换可以得到 str2 时,返回 True。 例如:
(3)编写函数 offByExtra(str1,str2),输入参数为两个非空的字符串 str1 和str2。仅当从 str1 中删除一个字符可以得到 str2,或者从 str2 中删除一个字符可以得到 str1 时,返回 True。 例如
(4)我们定义两个字符串 str1 和 str2 是邻居,仅当 str1 和 str2 不是同一个字符串, 并且三个函数 offByOne(str1,str2) , offBySwap(str1,str2) , offByExtra(str1,str2)中任意一个为 True。
按照上述定义,编写函数 ListOfNeighbors(str,L),输入参数 str 为字符串(要求全小写),L为字符串列表(见英文单词表 EnglishWords.txt),返回一个列表,为 L 中所有的str的邻居。
大致思路:
①offByOne(str1,str2)函数:
首先判断非法情况(两字符串相等,两字符串不等长,存在空字符串),对于非法情况直接返回False。对于合法情况,设定计数器,并对字符串进行遍历检查,记录下字符不同的数量并进行判断,如果计数器大于1则直接返回False。待字符串遍历完后,如果计数器的值为1,则返回True。
②offBySwap(str1,str2)函数:
首先判断非法情况(两字符串相等,两字符串不等长,存在空字符串),对于非法情况直接返回False。对于合法情况,如果进行暴力穷举,将花费更多时间,因此可以采用检测并尝试交换的方法将时间复杂度降至线性。设定标志变量列表,遍历字符串记录下不同字符的位置。当遍历结束后,如果标志变量列表的长度不为2,则直接返回False。判断交互字符是否对应相等,如果相等则返回True,否则返回False。
③offByExtra(str1,str2)函数:
首先判断非法情况(两字符串长度绝对值之差不等于1,两字符串不等长,存在空字符串),对于非法情况直接返回False。对于合法情况,定义变量记录不同字符的位置,遍历长度短的字符串进行比较并记录不同字符的位置。如果遍历完字符串后,标志变量仍为初始值,则说明仅有长字符串最后一个字符多余,则返回True;若标志变量不为初始值,则删去标志变量对应字符串的字符,并进行比较。如果相同则返回True,不相同返回False。
④统计结果并输出:
首先从输入中获取待判断的单词,并通过输入流加载整个EnglishWords.txt文件。并对文件进行预处理,将 文件按行读入列表后,去除列表元素中的空格以及换行。调用ListOfNeighbors函数进行判断,对于每个EnglishWords.txt文件中的单词,如果满足三个函数中的任意一个,则将其存入结果列表。处理完整个文件后返回结果列表。统计长度后进行输出。
源代码:
本题下,将分块依次展示源代码并进行讲解:
①offByOne(str1,str2)函数:
# 定义只有一个位置不同判定函数 def offByOne(str1, str2): # 直接去除非法情况(两字符串不等长,存在空字符串,两字符串相等) if len(str1) != len(str2) or str1 == str2 or len(str1) == 0 or len(str2) == 0: return False # 利用计数器进行计数,如果不一样的字符大于1则直接返回False count = 0 for i in range(len(str1)): if str1[i] != str2[i]: count += 1 if count > 1: return False return True
L4:对非法情况进行特判处理
L8-L13:定义计数器对不同字符进行判断,当出现两个及两个以上不同字符时,直接返回False。
L14:若只有一个不同的字符,则返回True
②offBySwap(str1,str2)函数:
# 定义判断交换判定函数 def offBySwap(str1, str2): # 直接去除非法情况(两字符串不等长,存在空字符串,两字符串相等) if len(str1) != len(str2) or str1 == str2 or len(str1) <= 1 or len(str2) <= 1: return False # 定义标志变量列表 flag = [] # 遍历进行查找 for i in range(len(str1)): if str1[i] != str2[i]: flag.append(i) # 如果存在大于2个不同,则返回False if len(flag) > 2: return False # 如果不同的数量不为2,则返回False if len(flag) != 2: return False # 判断并返回结果 return str1[flag[0]] == str2[flag[1]] and str2[flag[0]] == str1[flag[1]]
L4:对非法情况进行特判处理
L8-L18:设置标志变量并进行查找,遍历字符串并记录不同字符的下标存入标志变量列表中,并检测标志变量列表的大小,当大小大于2时,直接返回False。遍历完字符串后再检测标志变量列表的大小,如果不为2也返回False。
L21:最后判断交换的对应字符是否相等,如果相等返回True,否则返回False。
③offByExtra(str1,str2)函数:
# 定义删去一个判定函数 def offByExtra(str1, str2): # 直接去除非法情况(两字符串长度差的绝对值不等于1,存在空字符串,两字符串相等) if abs(len(str1)-len(str2)) != 1 or str1 == str2 or len(str1) == 0 or len(str2) == 0: return False # 记录不同字符位置 misIndex = -1 # 使str1为短字符串,方便后续处理 if len(str1) > len(str2): str1, str2 = str2, str1 # 遍历进行查找,并记录不同字符位置 for i in range(len(str1)): if str1[i] != str2[i]: misIndex = i break # 如果除了最后一个字符都相同,则返回True if misIndex == -1: return True # 利用字符切片,切去待删除字符 str2 = str2[:misIndex]+str2[misIndex+1:] # 进行判断并返回结果 return str1 == str2
L4:对非法情况进行特判处理
L10-L11:将str1变成较短字符串方便处理
L14-L17:遍历短字符串依次查找并记录不同字符位置。
L20-L21:特判最后一个字符相同的情况
L24-L27:删除对应不同字符,判断两字符串是否相等如果相等返回True,如果不相等则返回False
代码测试:
①测试‘leap’的邻居:
②测试‘abcd’的邻居:
③测试‘apple’的邻居:
④测试‘ban’的邻居:
⑤测试‘python’的邻居:
心得与体会:
①Python编程过程中要善于使用系统库函数,可以方便高效快捷正确的完成一些操作。
例如:第一题中,借助了zip对矩阵元素进行正向收集,高效的完成了矩阵转置操作。
②对于一些需要进行检测出现频率的过程,可以选用“字典”结构进行高效快捷地完成。
例如:第三题中,借助字典完成了频率判断的操作,方便又快捷。
③算法的优化是不可忽视的。
在第四题中,由于输入数据比较大,因此需要尽量降低运行时间。对于offBySwap函数,可以通过线性检测的方式时间复杂度降低为线性,从而避免暴力的平方型时间复杂度。
④数据特判是解决特殊数据的好手段
在第四题中,对一些不符合情况的数据进行特判,可以提高程序的容错率,并且一定程度上提高程序运行效率,降低运行所需时间。
此外,在本次实验过程中,所有代码中均有很详细的注释,方便理解代码的逻辑。