基于AC自动机的表白墙解析工具

1.引言

前两天是著名的 520 日,尽管在法定上,今天并没有特殊之处,但是对于广大情侣来说,520有着非凡的意义。通常来说,脱单的最直接方式就是表白,一个好的表白可以帮助我们迅速脱单,因此表白墙等社交平台也迅速发展,成为人们表白的不二之选。

据不准确统计,中国90%以上的高校都有官方或民间组织的表白墙平台,下图为上财表白墙的一角,每天可以公示同学们投稿的表白内容。表白墙对我们脱单也有很大的影响。一方面,我们可以在表白墙上高调表白心仪的小哥哥或小姐姐,另一方面,我们也可以时刻关注我们的目标是否被其他人表白,从而推断自己潜在的“情敌”,达到知己知彼的境界。

通常来说,我们往往在表白墙上只会关注一个名字,因此每天保持看表白墙的习惯,将会对脱单有很大的帮助。但是在以下几种情况下,我们可能要在表白墙上同时关注多个名字,可能的情况包括但不限于:

1. 我除了关心心仪的对象是否被表白,我也关心自己是否被表白

2. 我很渣,我同时关心多个心仪的对象是否被表白

3. 我很八卦,我除了关系与自己有关的人,还关注我的哥们/姐妹们是否被表白

4. 我比上面还八卦,我还关心还关注我的哥们/姐妹们,已经哥们的女朋友和姐妹的男朋友是否被表白

在这种情况下,我们的注意力可能是有限的,不能同时关注所有关心的人,因此在今天,雷帅实验室正式发布表白墙分析工具,让你不会错过任何一个在意的人!

2.相关问题描述及技术介绍

因此,我们的问题可以描述为,给定一段文本内容(表白墙内容)和一组关注的对象名字,匹配文本内容中是否包含所给对象名字列表中的一个或多个,或者一个也没有。此外,考虑到许多表白内容比较含蓄,并不包含直接的姓名,而是用拼音缩写影射,我们还需要将拼音影射到姓名上,提供合理的显示。

我们选用的算法是AC自动机算法,这是一种用于多模式匹配的算法,和KMP算法的思想有异曲同工之妙。一般来说,要理解AC自动机,先要理解KMP算法和Trie树,然而根据我的实践,KMP算法忘了的话,也问题不大。

今天时间有限,来不及过多介绍AC自动机的相关原理,代码可以从我的连接中直接获取。值得一提的是,其实我并不懂什么是自动机,虽然我很早就听说了自动机的概念,那是我刚上大学,还是个编程菜鸟的时候,跟一个国内顶尖高校类似专业的中学同学交流,偶然听说的。她说当时正在学自动机,我又不太好意思说没听过,上网百度一番,勉强还能继续这个话题。其实人还是谦虚点好。

3.系统实现

因此我们基于了web开发来实现上述功能。

首先我们收集当天的上财表白墙内容,这件事并不容易,因为微信公众号文章很难爬取。我们选用了另一种策略,即半手工录入,所谓半手工,是指管理员需要填写当天表白墙文章链接,有了这个链接,我们就可以爬取当天的所有表白内容。但是如果长期运营,就需要管理员每天提供表白墙链接,也很麻烦。

第二步,我们在页面上通过js实现AC自动机,来结合上一步爬取到的表白墙内容进行匹配标记,运算可以在前端完成,减轻服务器压力。

第三步,优化用户体验。我们允许用户输入感兴趣的一组人名,并将其保存在COOKIE中,用户重新加载页面时,不需要重新输入。

4.效果展示

为了展示效果,我们不得不化用两个刚好能匹配上的名字来展示。例如我们用“梁振英”和“王六学”两个名字来匹配昨天的表白墙。如图,经过匹配后的姓名或拼音缩写就会被标记为下拉菜单的样式,下拉可以看到所有可能匹配的名字。

为了节目效果,我也发了一条感谢耿哥的表白墙,来检测分析效果。

也没有问题!

5.系统连接

系统连接为

http://sufeguide.fgb2019.top/ac

6.郑重声明

1. 本系统中未经用户允许,我们绝不会擅自收集用户信息,所有信息将保存在用户浏览器的COOKIE上,除非用户同意,我们方可收集信息。

2. 目前为止账号除了接收极少量个人打赏,没有任何官方或民间组织资助。

7.尾声

有空了可以分享一下AC自动机的原理,不过今天就算了。在网上看到大佬用C写的AC自动机代码,美的一塌糊涂,分享一下


#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn =  2*1e6+9;

int trie[maxn][26]; //字典树
int cntword[maxn];  //记录该单词出现次数
int fail[maxn];     //失败时的回溯指针
int cnt = 0;

void insertWords(string s){
    int root = 0;
    for(int i=0;i<s.size();i++){
        int next = s[i] - 'a';
        if(!trie[root][next])
            trie[root][next] = ++cnt;
        root = trie[root][next];
    }
    cntword[root]++;      //当前节点单词数+1
}
void getFail(){
    queue <int>q;
    for(int i=0;i<26;i++){      //将第二层所有出现了的字母扔进队列
        if(trie[0][i]){
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }

//fail[now]    ->当前节点now的失败指针指向的地方
////tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
    while(!q.empty()){
        int now = q.front();
        q.pop();

        for(int i=0;i<26;i++){      //查询26个字母
            if(trie[now][i]){
                //如果有这个子节点为字母i+'a',则
//让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
                //有点绕,为了方便理解特意加了括号

                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else//否则就让当前节点的这个子节点
                //指向当前节点fail指针的这个子节点
                trie[now][i] = trie[fail[now]][i];
        }
    }
}


int query(string s){
    int now = 0,ans = 0;
    for(int i=0;i<s.size();i++){    //遍历文本串
        now = trie[now][s[i]-'a'];  //从s[i]点开始寻找
        for(int j=now;j && cntword[j]!=-1;j=fail[j]){
            //一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
            ans += cntword[j];
            cntword[j] = -1;    //将遍历国后的节点标记,防止重复计算
        }
    }
    return ans;
}

int main() {
    int n;
    string s;
    cin >> n;
    for(int i=0;i<n;i++){
        cin >> s ;
        insertWords(s);
    }
    fail[0] = 0;
    getFail();
    cin >> s ;
    cout << query(s) << endl;
    return 0;
}


转自 https://blog.csdn.net/bestsort/article/details/82947639
侵删

祝愿大家520快乐,有情人终成眷属!

欢迎关注我的公众号,我会在那里第一时间分享我制作的废品软件。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值