编辑距离实现代码查重(c++实现)

本次数据结构作业是要写一个两份代码查重的系统,还要简单的UI交互。写了几天上网查了好多资料,总算是写完了,写个博客记录下,也算打打编程基础了。

问题分析

编写程序判断给定的一批C源程序文件相互之间是否存在抄袭。程序需标注出有抄袭嫌疑的源代码文件之间相似段落。

从储存代码,提取语句,到计算重复度,展示重复语句,可以分为以下几个步骤。

① 读取代码文本,并保存在对应的数据结构中。

② 将文本并分割成若干个语句。

③ 将不同的语句比对,计算编辑距离(相似度)。

④ 将两份文本的所有语句一一比对,计算查重率。

⑤ 重复上述操作,直到完成所有代码文本的比对。

⑥ 设置ui交互,引导用户输入路径,完成代码查重。

数据结构设计

五个结构体, filename_list、Afile、sentence、node、match,分别保存

文件名链表、一份文件、一个句子、两份文件的匹配结果、两个语句的匹配结果。

* 一个文件夹对应一个文件名链表filename_list 里面有很多文件

* 一个文件Afile 里面有很多句子sentence

* 一个结点node有两份文件 记录这两份文件的查重信息

* 句子查重信息保存在match匹配结构体中 一个node有很多match

* 这里说的"很多"是一个链表 而上一级结构体会保存下一级链表的头指针

*

* 我们规定有两组代码 分别保存在两个文件名链表filename_list中

* A组一份代码文件会和B组所有代码文件一一匹配,匹配结果保存在node中

* A组所有文件都会重复上述操作 从而实现

* 一对一

* 一对多

* 多对一

* 多对多

* 混杂查重(当A组和B组文件相同时)

*

* 因此每个filename_list结点会保存一个node链表的头指针 将记录所有与另一组文件一一匹配的信息

* 一个node记录一个一对一结果

主要算法实现

    • 编辑距离

采用动态规划法,长度为s1和s2的字符串可以分为几种情况。

1)最后一个字符相同,可以转化为长度为s1-1的子串和长度为s2-1的字串编辑距离。

编辑次数不变。

2)最后一个字符不同

1》删除s1的最后一个字符 (变成s1-1的字串和s2计算)

2》将s1的最后一个字符变成和s2一样的字符 (相当于删除两个字串的最后一个字符,变成s1-1和s2-1的子串的计算)

3》增加一个和s2最后一个字符一样的字符(相当于删除s2的最后一个字符,变成s1和s2-1的子串计算)

无论是这三种的哪一种,编辑次数都+1。

经过递归处理,两个字串长度一定会越来越小,最后返回编辑次数最小的值,便是两个字符串s1 s2的编辑距离。

从后往前推,可以用循环来完成。核心为这三行代码

在此之前要对dp二维数组初始化

dp二维数组的第一行和第一列默认从0开始增加到59,从第二行和第二列才正式开始计算编辑距离。所以当为dp[60][60]时,两个字符串最长为59。dp[59][59]的值就是两个字符串的编辑距离。

2.从一个文件夹读取所有文件

上网查到的是用 _finddata_t 和 _findnext 可以遍历这个文件夹。简单一些我们就不深度遍历了。

3.读取一个文件的内容

我用的是fgetc()函数,一次读取一个,这样在遇到分号时切割成一个个语句,保留代码逻辑性。一句句代码保存在sentence结构体里,而这个函数返回一个Afile结构体的指针。

简单ui设计

我们这是个简单的系统,只要提示用户输入文件或者文件夹的路径就好了

1.利用printf打印一个简单界面。

2.利用#include <conio.h>库中的_getch()函数接受用户命令并交互。

3.设置while循环,重复接收用户命令。

如主界面

问题与思考

值得思考的问题

1.如何根据编辑距离判断两句子的重复度?

我们采用的方式是similar = [ 1 - (编辑距离/较长字符串的字符数) ] * 100%

短语句编辑次数越少就能成为长语句,就说明二者的相似度越高

缺点)如果抄袭句子故意加上一大段无关代码,编辑距离和较长字符串数就会同时增大,相似度就会变得比较低

改进)我们采用的是以分号(;)分割语句,保留语句逻辑性,计算编辑距离时加上无关代码的难度就比较高(因为加代码通常要分号分隔)

2.计算完编辑距离后,怎么利用相似度计算查重率?

我们采用的方式是设置阈值,相似度高于0.4的判定为相似语句,相似语句和文本语句总量的占比就是查重率

有以下不足

1)阈值不好确定

2)根据占比,基本上不可能有100%查重率的代码

优点

1)设置简单

2)可以根据查重率再设置阈值,>80%基本上可以认定为完全抄袭 >60%的认定为大量抄袭,而无关代码的查重率通常低于20%,符合我们的认知

3.两文本匹配方式

知道两字符串的匹配方式,怎么利用到文本中去呢?

我们采用的方式是暴力匹配,a文件的每个语句都会和b文件的所有语句匹配,找到相似度最高的语句。

缺点

1)时间复杂度高 为O(n*n)

2)可能出现多语句反复匹配一句的现象

优点

1)实现方便

2)结果较准确 即使有少量误判情况 两份无关代码a b查重率也会低于25%

4.两语句故意更改变量名降低编辑距离怎么办?

这是尚未处理的问题,可参考的思路有

1)变量名归一化,长的变量名全部变为a1 a2...

实现困难 难免有短的变量名a ,不能把文本中所有的a字符变为归一化后的字符

5.动态规划法计算编辑距离的问题

当字符串的长度比较长时(超过100)二维数组dp[100][100]就可能会栈溢出

而动态再堆区开辟二维数组实现较困难

而用递归的方式动态规划成本较高,也有可能溢出

解决:将字符串拆成50字符的若干个子串,将子串一一匹配

1)需要内存较小,只需dp[50][50]即可

2)降低了时间复杂度

3)效果与真实计算结果相近

总结:通过这次编程还是学到了很多东西,对c/c++的一些函数操作更熟悉了一些,希望以后继续多写代码,多写博客记录一些实现流程和思考。

源代码

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值