【暖*墟】 #trie# 字典树的运用

本文深入探讨了字典树Trie的原理与应用,详细介绍了Trie的初始化、插入和检索操作,以及如何利用Trie解决实际问题,包括电话簿、按位异或最大值、即时解码可能性、L语言理解和多个竞赛题目解析。

Trie,又称字典树

是一种用于实现字符串快速检索的多叉树结构。

每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到一个字符c ,

沿着当前节点的c这个字符指针,走向该指针指的节点

下面我们来详细讨论Trie的基本操作过程。

初始化

一棵空Trie仅包含一个根节点,该点的字符指针均指向空

*********  根节点表示空串。*********

插入

当需要插入一个字符串S时,我们令一个指针P起初指向根节点

然后,依次扫描S中的每个字符c。

1.若P的c字符指针指向一个已经存在的节点Q,则令P=Q。

2.若P的c字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q,然后令P=Q

当S中的字符扫描完毕时,在当前节点P上标记它是一个字符串的末尾

检索

当需要检索一个字符串S在Trie中是否存在时,我们令一个指针P起初指向根节点,

然后依次扫描S中的每个字符c:

1.若P的c字符指针指向空,则说明S没有被插入过Trie,结束检索。

2.若P的的c字符指针指向一个已经存在的节点Q,则令P=Q

当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,

则说明S在Trie中存在,否则说明S没有被插入过Trie。

即先将字符传入字典树中,查询的时候从根节点向下层寻找(每层26个字母)。

任意一个线结点所表示的字符串,都是实际字符串集合中某些串的前缀。

——> 利用串的公共前缀,节约空间。可以看做26叉树(全是小写英文字母)。

    bool tail[SIZE]; //串尾元素
    int trie[SIZE][26],tot=1; //SIZE:字符串最大长度(层数)
    //tot为节点编号,用它可以在trie数组中表示某层的某字母是否存在
     
    void insert(char* str){ //插入一个字符串
    int len=strlen(str),p=1; //p初始化为根节点
    for(int k=0;k<len;k++){
    int ch=str[k]-'a'; //小写字符组成串的某个字符,变成数字
    if(trie[p][ch]==0) trie[p][ch]=++tot; //trie存编号tot
    //↑↑↑不存在此层的这个字符,新建结点,转移边
    p=trie[p][ch]; //指针移动,连接下一个位置
    }
    tail[p]=true; //s中字符扫描完毕,p标记的是字符串的末位字符(的编号)
    }
     
    bool searchs(char* str){ //检索字符串是否存在
    int len=strlen(str),p=1; //p初始化为根节点
    for(int k=0;k<len;k++){
    p=trie[p][str[k]-'a']; //寻找下一处字符
    //trie[lastp][ch1]=nowp;
    //trie[nowp][ch2]=nextp;
    //nowp=trie[nowp][ch2]=nextp; 即寻找下一处字符的编号
    if(p==0) return false; //某层字符没有编号,不存在,即串也不存在
    }
    return tail[p]; //判断最后一个字符
    }
View Code

 

字典树trie 例题

(1)【电话簿】 poj 3630

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
 
/*【电话簿】 poj 3630
多组数据,每组n个长度不超过10的数字串,
问是否存在数字串S,T。使得S是T的前缀。  */
 
//构建Trie,过程中可以顺便判断答案
 
const int maxn=1e5+9;
int trie[maxn][10]; //数字只有0~9
int n,tot,T;
bool tail[maxn];
char s[20]; //每个串长度不超过10
 
void clears(){ //初始化清零
    memset(trie,0,sizeof(trie));
    memset(tail,false,sizeof(tail));
}
 
bool insert(char* s){ //插入一个字符串T,判断S是不是它的前缀
    int len=strlen(s),p=1; //p初始化为根节点
    bool flag=false;
    for(int k=0;k<len;k++){
        int ch=s[k]-'0'; //小写字符组成串的某个字符,变成数字
        if(trie[p][ch]==0) trie[p][ch]=++tot; //trie存编号tot
        //↑↑↑不存在此层的这个字符,新建结点,转移边
        else if(k==len-1) flag=true; //没有插入任何新节点
        p=trie[p][ch]; //指针移动,连接下一个位置
        if(tail[p]) flag=true; //经过某个有标记的结点,即完成了某个串
    }
    tail[p]=true; //s中字符插入完毕,p标记的是字符串的末位字符(的编号)
    return flag;
}
 
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n); tot=1; //tot是编号数
        clears(); bool ans=false;
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            if(insert(s)) ans=true; 
        }
        if(ans) puts("YES");
        else puts("NO");
    }
    return 0;
}

 

 

(2)【按位异或】 bzoj 4260

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
 
/*【按位异或】 bzoj 4260 */
 
/*【分析】设l[i]表示1<=l<=r<=i的最大a[l]^a[l+1]^...^a[r],
r[i]表示i<=l<=r<=n的最大a[l]^a[l+1]^...^a[r],
则结果为最大的l[i]+r[i+1]。问题转化为求l[i]和r[i]。
设x[i]表示a[0]^a[1]^...^a[i](a[0]=0),那么l[i]=max(x[i]^x[j])(j<i) */
 
/*【求一组数中两个数 异或 得到的最大值】
把每个整数看成长度为31的二进制01串(在前面补0),
并把31位的串插进一棵trie树(最低二进制位为叶节点),
进行类似查找的过程:沿着“与当前位相反的字符指针”向下层访问。
如果“与ai当前位相反的字符指针”指向空节点,就访问ai当前位指针。
根据xor运算“相同=0,不同=1”,求出异或值最大的ai和aj。 */
 
const int maxn=4e5+9;
int trie[maxn<<5][2]; //数字只有0~9
int n,tot=1,l[maxn],r[maxn],a[maxn];
 
void clears(){ //初始化清零
    memset(trie,0,sizeof(trie));
}
 
void insert(int x){ //插入一个二进制数
//将当前的前/后缀异或和化为31位二进制数,插入trie树
    int p=1; //p初始化为根节点
    for(int i=1<<30;i;i>>=1){ //每次除二
        int c=(x&i)? 1:0;
        if(trie[p][c]==0) trie[p][c]=++tot; //trie存编号tot
        //↑↑↑不存在此层的这个字符,新建结点,转移边
        p=trie[p][c]; //指针移动,连接下一个位置
    }
}
 
int finds(int x){ //找目前最大的两数异或和
    int p=1,ans=0;
    for(int i=1<<30;i;i>>=1){ //从高位到低位,尽量让异或和高位为1
        int c=(x&i)? 0:1; //每一位反过来
        if(trie[p][c]){ //这一位记录过,可以继续
            ans+=i; p=trie[p][c]; //查询与相反相连的下一位
        }
        else p=trie[p][!c]; //否则这一位只能为0,连接与相同相连的下一位
    }
    return ans;
}
 
int main(){
    int now,ans=0;
    scanf("%d",&n); insert(now=0); //存a[0]的异或值
 
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        now^=a[i]; //now为a[0]~a[i]的异或和
        insert(now);
        l[i]=max(l[i-1],finds(now));
        //l[i]表示1<=l<=r<=i的最大a[l]^a[l+1]^...^a[r]
    }
    
    clears(); tot=1; insert(now=0); //重新初始化
    for(int i=n;i>=1;i--){
        now^=a[i]; //now为a[n]~a[i]的异或和
        insert(now);
        r[i]=max(r[i+1],finds(now));
        //r[i]表示i<=l<=r<=n的最大a[l]^a[l+1]^...^a[r]
    }
 
    for(int i=1;i<n;i++)
        ans=max(ans,l[i]+r[i+1]);
    printf("%d\n",ans);
    return 0;
}

 

(3)【IMMEDIATE DECODABILITY】poj 1056

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
 
/*【IMMEDIATE DECODABILITY】poj 1056
给你一些字符串,每组数据以9结尾,判断给出的字符串中,
是否存在一个字符串是另外一个字符串的前缀(如:01为010的前缀);
如果存在至少一组这种情况输出:Set %d is not immediately decodable ;
不存在则输出:Set %d is immediately decodable 。 */
 
//构建Trie,过程中可以顺便判断答案
 
const int maxn=1e5+9;
int trie[maxn][10]; //数字只有0~9
int tot=1;
bool tail[maxn];
char s[20]; //每个串长度不超过10
 
void clears(){ //初始化清零
    memset(trie,0,sizeof(trie));
    memset(tail,false,sizeof(tail));
}
 
bool insert(char* s){ //插入一个字符串T,判断S是不是它的前缀
    int len=strlen(s),p=1; //p初始化为根节点
    bool flag=false;
    for(int k=0;k<len;k++){
        int ch=s[k]-'0'; //小写字符组成串的某个字符,变成数字
        if(trie[p][ch]==0) trie[p][ch]=++tot; //trie存编号tot
        //↑↑↑不存在此层的这个字符,新建结点,转移边
        else if(k==len-1) flag=true; //没有插入任何新节点
        p=trie[p][ch]; //指针移动,连接下一个位置
        if(tail[p]) flag=true; //经过某个有标记的结点,即完成了某个串
    }
    tail[p]=true; //s中字符插入完毕,p标记的是字符串的末位字符(的编号)
    return flag;
}
 
int main(){
    int kase=1,flag=0;
    while(scanf("%s",s)!=EOF){
        if(s[0]=='9'){
            if(!flag) printf("Set %d is immediately decodable\n",kase++);
            else printf("Set %d is not immediately decodable\n",kase++);
            flag=0; tot=1; clears(); continue;
        }
        if(!flag&&insert(s)) flag=1;
    }
    return 0;
}

 

(4)【L语言】bzoj 1212

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
 
/*【bzoj 1212】L语言
标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。
现在你要处理的就是一段没有标点的文章。一段文章T是由若干小写字母构成。
一个单词W也是由若干小写字母构成。一个字典D是若干个单词的集合。 
我们称一段文章T在某个字典D下是可以被理解的,是指如果文章T可以被分成若干部分,
且每一个部分都是字典D中的单词。 例如字典D中包括单词{‘is’, ‘name’, ‘what’, ‘your’},
则文章‘whatisyourname’是在字典D下可以被理解的,因为它可以分成4个单词:‘what’, ‘is’, ‘your’, ‘name’,
且每个单词都属于字典D,而文章‘whatisyouname’ 在字典D下不能被理解,
但可以在字典D’=D+{‘you’}下被理解。这段文章的一个前缀‘whatis’,
也可以在字典D下被理解 而且是在字典D下能够被理解的最长的前缀。 
给定一个字典D,你的程序需要判断若干段文章在字典D下是否能够被理解。 
并给出其在字典D下能够被理解的最长前缀的位置。  */
 
//【分析】令f[i]表示到i的前缀能否被理解,那么答案就是f[i]==1时最大的i。
// 如果f[i]==1,这个串就可以从i+1开始匹配一个新单词,设trie上匹配到一个长度为j的单词,那么f[i+j]=1。
 
const int maxn=(1<<20)+10;
int n,m,len,f[maxn];
char s[maxn];
 
struct trie{
    int tot,ch[2010][26];bool ok[maxn];
    void insert(){
        int p=0,len=strlen(s);
        for(int i=0;i<len;p=ch[p][s[i]-'a'],i++) 
            if(!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++tot;
        ok[p]=1; //队尾标记
    }
}T;
 
int work(int t){
    int ans=0; len=strlen(s+1);
    f[0]=t;
    for(int i=0;i<=len;i++){
        if(f[i]!=t) continue;
        else ans=i;
        for(int p=0,j=i+1;j<=len;j++){
            p=T.ch[p][s[j]-'a'];
            if (!p) break;
            if (T.ok[p]) f[j]=t;
        }
    }
    return ans;
}
 
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) 
        scanf("%s",s),T.insert();
    for(int i=1;i<=m;i++) 
        scanf("%s",s+1),printf("%d\n",work(i));
    return 0;
}

 

(5)bzoj 1590 / bzoj 4567 / bzoj 1954

 

 

                                                                             ——时间划过风的轨迹,那个少年,还在等你。

转载于:https://www.cnblogs.com/FloraLOVERyuuji/p/9346737.html

内容概要:本文提出了一种基于改进扩散模型的高海拔地区新能源高波动出力场景生成方法,并提供了完整的Python代码实现。该方法针对高海拔地区风能、光伏等新能源出力波动剧烈、不确定性高的特点,通过优化扩散模型的结构与训练策略,有效捕捉历史数据的概率分布特征与时序相关性,从而生成高质量、多样化的出力场景。文中详细阐述了模型的数学推导、网络架构设计、损失函数优化及采样算法改进,并通过实验证明其在拟合精度、场景多样性与稳定性方面优于传统生成模型,为电力系统在高比例新能源接入下的规划、调度与风险评估提供了可靠的场景输入支持。; 适合人群:具备一定Python编程能力和机器学习基础,从事新能源发电预测、电力系统分析、智能优化、场景生成等方向研究的科研人员、高校研究生及工程技术人员。; 使用场景及目标:①用于高海拔地区风电、光伏出力的不确定性建模与多场景生成;②支撑含高渗透率新能源的电力系统随机优化调度、鲁棒决策与风险评估;③为相关学术研究、论文复现与算法改进提供可运行的技术方案与代码基础; 阅读建议:建议读者结合所提供的完整资源(代码、数据集、说明文档)进行实践操作,重点关注扩散模型的前向加噪与反向去噪过程的设计细节,以及如何将其适配于新能源时序数据的生成任务,通过参数调优与对比实验深入理解模型的生成机制与性能边界。
内容概要:本文围绕基于静态约束法的配电网电动汽车接入容量评估展开研究,提出了一种在新型电力系统背景下评估主动配电网对电动汽车承载能力的方法。研究通过构建数学模型,结合潮流计算与关键约束条件(如电压越限、线路过载等),量化分析配电网可承受的最大电动汽车充电负荷容量,旨在识别规模化电动汽车接入带来的潜在运行风险,并为电网规划与运行提供科学依据。文中配套提供了完整的Matlab代码实现,便于仿真验证与结果复现。此外,该研究与分布式光伏承载力评估、电动汽车可调能力分析等方向形成技术联动,展现了多主题协同的研究体系。; 适合人群:具备电力系统分析基础理论知识及Matlab编程能力的高校研究生、科研机构研究人员,以及从事新能源并网、智能配电网规划与运行等相关领域的工程技术人员。; 使用场景及目标:①用于学术研究中的模型复现与论文撰写支撑;②评估实际配电网中电动汽车大规模接入的可行性与安全边界,指导充电基础设施布局;③作为高校教学案例,帮助学生深入理解电网承载力评估的核心原理、建模方法与仿真技术; 阅读建议:建议结合文中提及的相关研究方向(如二阶锥规划、多面体聚合方法等)进行对比学习,充分利用所提供的Matlab代码与网盘资料开展仿真实验,重点关注约束条件的设定逻辑与潮流计算模块的实现细节,以深化对评估模型机理与工程应用价值的理解。
内容概要:本文围绕“考虑隐私保护的分布式联邦学习电力负荷预测研究”展开,提出了一种基于Python实现的联邦学习框架,旨在解决居民或行业电力负荷预测中用户电表数据隐私泄露的风险。该研究通过构建分布式机器学习模型,使各参与方在不共享原始数据的前提下协同训练全局模型,有效实现了数据“可用不可见”。文中详细阐述了联邦学习的整体架构设计、本地模型训练流程、参数加密传输与安全聚合机制,并结合差分隐私等技术进一步增强系统的隐私保护能力。同时,研究利用真实电力负荷数据集进行了实验验证,展示了方法在预测精度与隐私保障之间的良好平衡,并提供了完整的代码实例与复现指南,便于后续研究与应用拓展。; 适合人群:具备一定机器学习基础和电力系统背景知识,从事智慧能源、隐私计算或人工智能相关方向研究的研究生、科研人员及工程技术人员。; 使用场景及目标:① 实现跨区域、跨主体的电力负荷协同预测,打破数据孤岛;② 在确保用户用电数据隐私安全的前提下提升负荷预测准确性;③ 推动联邦学习在智能电网、需求响应、虚拟电厂等场景中的实际部署与应用。; 阅读建议:建议结合文中提供的Python代码与网盘资料进行动手实践,重点关注联邦学习的通信轮次设计、模型聚合算法(如FedAvg)的实现细节以及差分隐私噪声添加策略,深入理解其对模型性能与隐私强度的影响,为进一步优化与创新奠定基础。
VDA_Band_19.1_3rd edition_2026 English Inspection of Technical Cleanliness 内容概要:本文档为德国汽车工业协会(VDA)发布的第三版《技术清洁度检验:功能相关汽车部件的颗粒污染检测》(VDA 19.1),系统规范了汽车行业中零部件技术清洁度的检测方法与流程。文件涵盖从取样、提取、过滤到分析的全流程标准化操作,重点更新了干法提取(如 Stamp Test 和刷吸法)、小于50µm颗粒的检测、光学子系统和SEM/EDX标准分析方法,并引入统一材料分类体系以提升结果可比性。同时明确了“标准分析”与“自由检验”的区别,前者用于高兼容性检测,后者允许客户与供应商协商定制参数。文档还强化了对非可测组件的技术清洁保障、测量不确定度评估及方法验证的要求,并提供了多个实际案例支持应用落地。; 适合人群:适用于汽车制造业中从事质量控制、工艺开发、供应商管理及相关检测实验室的技术人员和管理人员,尤其适合具备一定质量管理或洁净度检测基础的专业人员。; 使用场景及目标:①用于制定和执行零部件清洁度检测标准;②指导 incoming/outgoing 检验及生产过程监控;③支持失效分析与质量改进项目;④作为企业内部审核和技术交流的依据; 阅读建议:建议结合VDA 19.2及其他相关标准配套使用,重点关注各章节中的起始参数设定、方法选择逻辑及附录中的检查表示例,在实际操作中同步开展方法验证与人员培训,确保检测结果的有效性和可追溯性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值