我成了五个大佬的祖宗_我家可能有位大佬

我成了五个大佬的祖宗_我家可能有位大佬Lumpy_Trie 详解 —— 由Ciyang大佬发明(不一定是首次)

大家好,又见面了,我是你们的朋友全栈君。


原题解地址及本文目的

https://ciyang.blog.luogu.org/solution-p2580

本文目的:留作日后自用,翻译一下大佬清奇的码风。


正文

Lumpy_Trie是边压缩的Trie, 可以省空间, 各Node存的是字符串。

现在来翻译(解释及简化(我是懒癌))一下Ciyang的源码。(源码及原注释在Ciyang神犇的题解里, 这里的注释是我自己加的, 这里的码是我抄的, 不保证完全一致)

先翻译节点定义

//这是Ciyang的define #define clear(a) memset(a, 0, sizeof a) #define copy(a, b) memcpy(a, b, sizeof a) //这是Ciyang的节点定义 char allstr[10001][51];//这是Ciyang的腐竹内存 char tmp[51];//这是本文中不会用到的东西 struct Lumpy_Tnode { const char *pStr;//指针,指向辅助内存中的地址,即各Node保存的字符串都是存在腐竹内存中的 int length, isEnd;//length就是本节点中存的字符串的长度,即pStr后多少位, isEnd就和Trie的“终结标记”一样(isEnd的定义浅显易懂 Lumpy_Tnode *children[26];//这是子节点,像Trie一样, 存储后继节点的地址 inline Lumpy_Tnode() { pStr = 0, length = isEnd = 0, clear(children);//这段构造函数是用在根节点上的 } inline Lumpy_Tnode(const char *str, int len, int end) { pStr = str, length = len, isEnd = end, clear(children);//这段构造函数是用在除根节点之外的节点上的 } } mNode;//这个我想是 main Node, 即 root Node 

接下来翻译insert函数

//这个函数在主程序里这样调用 insert(要插入的串, 要插入的串的长度, mNode(root)); inline void insert(const char *str, int length, Lumpy_Tnode *bNode) { //bNode是当前节点, 和Trie完全一样, str就是指针啦 if(!length) { bNode->isEnd = 1; return; } //建议先看后面的翻译 int ch = str[0] - 'a';//这个就是懒癌的象征了, 当然确实快 if(bNode->children[ch]) { //已经存在以 str[0] 为首字母的后继子串,请看下面翻译 bNode = bNode->children[ch];//转移焦点,开始协调 register int sptr = 0;//指针 while(sptr < length && sptr < bNode->length//循环来找当前字符串和节点存储的字符串最长前缀(Ciyang的注释 && bNode->pStr[sptr] == str[sptr] ) ++sptr; if(sptr != bNode->length) { //当节点存储的字符串不是插入字符的子串时……(由上面那个while的结束条件表明 //于是就要将最大公共前缀变成此节点, 两个后缀都变成子节点 Lumpy_Tnode *nNode = new Lumpy_Tnode(bNode->pStr + sptr, bNode->length - sptr, bNode->isEnd); //上一行那句就是讲此节点的后缀拆一出来作为子节点,此时原节点的子节点信息应被继承 copy(nNode->children, bNode->children); // 这就是继承了, 为什么继承大家都清楚 bNode->isEnd = 0, bNode->children[bNode -> pStr[sptr] - 'a'] = nNode; //此时新节点要接到父节点上 } //以下应该是代码的简化, 如果想看的清楚明白一点就把以下代码加个完全复制到上面那个if里; bNode->length = sptr; insert(str+sptr, length - sptr, bNode); //将去掉与原bNode公共前缀的str插入 } else//并不存在以 str[0] 为首字母的后继子串(第一次的象征 bNode->children[ch] = new Lumpy_Tnode(str, length, 1); //于是就要新建节点, 并把整个串当数据 return; }

以下翻译find函数(简单多了

inline int find(const char *str, int length, Lumpy_Tnode *bNode) { if(!length) { if(bNode->isEnd == 1) return bNode->isEnd++;//这句带有题目的局限性,用时应怎么写大家都清楚 return bNode->isEnd; } int ch = str[0] - 'a'; if(bNode->children[ch]) { bNode= bNode->children[ch]; if(length < bNode->length) return 0; //自带剪枝,若当前查找字符串长度小于当前公共前缀,那么字典树中不存在当前查找的字符串(Ciyang的注释 //好吧, 我的解释:当前查找的字符串若存在(被插入过), 那么公共前缀一定比当前串长短或等长 register int sptr = 0; while(sptr < bNode->length && bNode->pStr[sptr] == str[sptr]) ++sptr; if(sptr != bNode->length) return 0; //最长公共前缀必须是当前查找的字符串的子串(Ciyang的注释) // 即……好吧看while条件吧,写不下去了 return find(str+sptr, length-sptr, bNode); //无需解释(写不下去了啊啊啊 } //这里可以加个else,更浅显 return 0; } 

以下是以我的码风(受Ciyang影响极深, 并认为Ciyang马蜂简洁的我的马蜂)抄写的Ciyang代码(luogu P2580

#include<bits/stdc++.h> using namespace std; #define clear(a) memset(a, 0, sizeof a) #define copy(a, b) memcpy(a, b, sizeof a) struct node{ const char *Sp; int len, isEnd; node *ch[26]; node() { Sp = 0, len = isEnd = 0, clear(ch); } node(const char *str, int length, int end) { Sp = str, len = length, isEnd = end, clear(ch); } } root; char AllStr[10001][60], s[60]; void insert(const char *str, int length, node* u) { if(!length) { u->isEnd = 1; return; } int v = str[0] - 'a'; if(u->ch[v]) { u = u->ch[v]; register int sptr = 0; while(sptr < u->len && sptr < length && str[sptr] == u->Sp[sptr]) ++sptr; if(sptr != u->len) { node *nNode = new node(u->Sp + sptr, u->len - sptr, u->isEnd); copy(nNode->ch, u->ch), clear(u->ch); u->isEnd = 0, u->ch[u->Sp[sptr] - 'a'] = nNode; } u->len = sptr; insert(str+sptr, length - sptr, u); } else u->ch[v] = new node(str, length, 1); return; } int find(const char *str, int length, node* u) { if(!length) { if(u->isEnd == 1) return u->isEnd++; return u->isEnd; } int v = str[0] - 'a'; if(u->ch[v]) { u = u->ch[v]; if(length < u->len) return 0; register int sptr = 0; while(sptr < u->len && str[sptr] == u->Sp[sptr]) ++sptr; if(sptr != u->len) return 0; return find(str + sptr, length - sptr, u); } return 0; } int main() { int n, m; scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%s", AllStr[i]); insert(AllStr[i], strlen(AllStr[i]), &root); } scanf("%d", &m); for(int i = 1; i <= m; ++i) { scanf("%s", s); switch(find(s, strlen(s), &root)) { case 0: cout << "WRONG\n";break; case 1: cout << "OK\n";break; case 2: cout << "REPEAT\n";break; } } return 0; }

转载于:https://www.cnblogs.com/tztqwq/p/11088418.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/106753.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Elasticsearch增、删、改、查操作深入详解

    Elasticsearch增、删、改、查操作深入详解引言:对于刚接触ES的童鞋,经常搞不明白ES的各个概念的含义。尤其对“索引”二字更是与关系型数据库混淆的不行。本文通过对比关系型数据库,将ES中常见的增、删、改、查操作进行图文呈现。能加深你对ES的理解。同时,也列举了kibana下的图形化展示。ESRestfulAPIGET、POST、PUT、DELETE、HEAD含义:1)GET:获取请求对象的当前状态。2)POST:改变对象的当前

    2022年4月30日
  • Android签名

    Android签名

    2021年8月10日
  • 软件工程 — 数据流图的画法

    软件工程 — 数据流图的画法1.数据流图的画法1.1数据流图的概念数据流图(DFD)是一种图形化技术,它描绘信息流和数据从输入移动到输出的过程中所经受的变换。说明:在数据流图中没有任何具体的物理部件,它只是描绘数据在软件中流动和被处理的逻辑过程。数据流图是系统逻辑功能的图形表示,即使不是专业的计算机技术人员也容易理解它,因此是分析员与用户之间极好的通信工具。此外,设计数据流图时只需考虑系统必须完成的基本逻辑功能,完全不需要考虑怎样具体地实现这些功能,所以它也是今后进行软件设计的很好的出发点。1.2

    2022年6月16日
  • Win10专业版如何用命令来查看激活信息

    Win10专业版如何用命令来查看激活信息现在网购非常方便,不少用户会选择在网上直接购买组装好的Win10电脑,因为是商家直接配置安装,所以你根本不知道系统是否有激活,可能出现未激活的状况,那么要如何查看电脑的激活状态呢?下面小编就这个问题跟大家分享一下Win10专业版如何用命令来查看激活信息的方法,有需要的朋友快来了解一下吧!Win10专业版用命令来查看激活信息的方法详细如下:一、“win+R“,开启运行窗口,输入“cmd”,回车。二、我们可以使用slmgr.vbs来获得系统的激活信息。1)slmgr.vbs-dlv

    2022年5月7日
  • JavaScript如何判断一个值是不是数字?「建议收藏」

    JavaScript如何判断一个值是不是数字?「建议收藏」第一种方法:isNaN()使用js自带全局函数isNaN(),isNaN()返回一个Boolean值,如下:varc="hello";//字符串isNaN(c);//返回一个false;varc=10;//数字inNaN(c);//返回一个true如果以上c为一个空串或是一个空格,isNaN将把c当作数字0来处理,所以检查不严谨。第二种方法:正则表达式functioncheckNu…

    2022年4月20日
  • 国内十大正规现货交易平台排名(2021版榜单)

    国内十大正规现货交易平台排名(2021版榜单)现货亦称实物,指可供出货、储存和制造业使用的实物商品。可供交割的现货可在近期或远期基础上换成现金,或先付货,买方在极短的期限内付款的商品的总称。期货的对称。随着互联网的出现,世界已慢慢变成地球村,建立在信息化基础上的现货电子交易走上新经济的舞台。现货电子交易(也称为大宗商品电子交易,或现货仓单交易)是以现货仓单为交易的标的物,采用计算机网络进行的集中竞价买卖,统一撮合成交,统一结算付款,价格行情实时显示的交易方式。国内不少投资者对现货交易还不是太了解,下面小编为您介绍“国内十大正规现货交易平台排名(20

    2022年6月15日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号