相关代码已经上传GitHub : https://github.com/EricLi404/go-utils
0x00 概述
在做在做反垃圾账号业务的时候,检测发现有几十名用户都在某个IP上有过行为打点。
# 原始数据
꧁❥?➦SAMI?MMS➣?❥꧂
꧁❥?➦Ⓜ️AHI?MMS➣?❥꧂..
jgcch7b?V?O?I?
2kc7fd7?V?O?I?
꧁❥?➦VIDYA?MMS➣?❥꧂
RcR?Raden??F
RcR?Raden?B
꧁❥?➦RESMA?MMS➣?❥꧂
Sanny8_star197
꧁❥?PARI?MMS➣?❥꧂
❁════❃MAHAKAL❃══7══❁
꧁❥?BIPASHA?MMS➣?❥꧂
.꧁❥?ISRAT?MMS?꧂
ek8660?V?O?I?
a8h9163?V?O?I?
꧁❥?➦NIRA?➣?❥꧂
꧁❥?PARINEETI?MMS?꧂
RcR?Raden?J
dem3ba2?V?O?I?
ggk5b27?V?O?I?
?RcR?Raden??
可以很明显的感知到其中部分账号存在一定的相似性,现需要将这几十名用户按相似性分为若干组,因此提出了一种基莱文斯坦距离和并查集结构的算法,来实现将一组字符串按相似度提取出若干不重复组的功能。
0x01 理论基础
莱文斯坦距离
Wikipedia:
莱文斯坦距离,又称Levenshtein距离,是编辑距离的一种。指两个字串之间,由一个转成另一个所需的最少编辑操作次数。允许的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
例如将kitten一字转成sitting:
- sitten (k→s)
- sittin (e→i)
- sitting (→g)
则两者之间莱文斯坦距离为3.
俄罗斯科学家弗拉基米尔·莱文斯坦在1965年提出这个概念。其用途主要有 DNA分析、拼写检查、语音辨识、抄袭侦测 。
并查集
Wikipedia:
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
关于并查集的应用,有个经典的朋友圈问题
:
有
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9
10个人,其中[0,1], [0, 4], [1, 2], [1, 3], [5, 6], [6, 7], [7, 5], [8, 9]
互为朋友,求该10个人中有几个朋友圈(二度以内朋友)。
计算方式如下:
- 首先列出所有的用户,然后给每个用户都分为单独的一组
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | B | C | D | E | F | G | H | I | J |
- 然后遍历一度朋友关系
[0,1], [0, 4], [1, 2], [1, 3], [5, 6], [6, 7], [7, 5], [8, 9]
,将每组一度关系更新为同一组(将一度关系后者的组更新为前者的组):
[0,1]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | C | D | E | F | G | H | I | J |
[0, 4]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | C | D | A | F | G | H | I | J |
[1, 2]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | A | D | A | F | G | H | I | J |
[1, 3]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | A | A | A | F | G | H | I | J |
[5, 6]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | A | A | A | F | F | H | I | J |
[6, 7]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | A | A | A | F | F | F | I | J |
[7, 5]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | A | A | A | F | F | F | I | J |
[8, 9]
人 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
组 | A | A | A | A | A | F | F | F | I | I |
- 至此,朋友圈已分出,也即
[0,1,2,3,4]
为一个朋友圈[5,6,7]
为一个朋友圈[8,9]
为一个朋友圈
0x02 main
- 基于莱文斯坦距离计算两个字符串之间的相似度
公式为:
Similarity(str1,str2) = Levenshtein(str1,str2)*2 / len(str1) + len(str2)
- 将莱文斯坦距离小于阈值的两个字符串记为连通(类似于朋友圈问题中的一度朋友), 得到如下列表:
str1 | str2 | Similarity |
---|---|---|
꧁❥?➦SAMI?MMS➣?❥꧂ | ꧁❥?➦Ⓜ️AHI?MMS➣?❥꧂… | 0.123457 |
kcfd?V?O?I? | ah?V?O?I? | 0.181818 |
RcR?Raden?B | RcR?Raden?J | 0.176471 |
RcR?Raden?B | ?RcR?Raden?? | 0.146341 |
ah?V?O?I? | demba?V?O?I? | 0.222222 |
꧁❥?➦NIRA?➣?❥꧂ | ꧁❥?PARINEETI?MMS?꧂ | 0.358209 |
RcR?Raden?J | ?RcR?Raden?? | 0.195122 |
… | … | … |
demba?V?O?I? | ggkb?V?O?I? | 0.170213 |
- 利用并查集将字符串分组,得到以下分组情况
꧁❥?BIPASHA?MMS➣?❥꧂
꧁❥?➦SAMI?MMS➣?❥꧂
.꧁❥?ISRAT?MMS?꧂
꧁❥?➦Ⓜ️AHI?MMS➣?❥꧂..
꧁❥?➦VIDYA?MMS➣?❥꧂
꧁❥?➦RESMA?MMS➣?❥꧂
꧁❥?PARI?MMS➣?❥꧂
꧁❥?➦NIRA?➣?❥꧂
꧁❥?PARINEETI?MMS?꧂
RcR?Raden??F
RcR?Raden?B
RcR?Raden?J
?RcR?Raden??
jgcchb?V?O?I?
kcfd?V?O?I?
ek?V?O?I?
ah?V?O?I?
demba?V?O?I?
ggkb?V?O?I?
Sanny8_star197
❁════❃MAHAKAL❃══7══❁
0x03 GitHub
相关代码已经上传Github : https://github.com/EricLi404/go-utils