哈希,kmp,trie树和AC自动机
1.哈希
字符串哈希实际上就是把一个字符串转化为一个数字
比如
然后就没啥了。。。
关于自然溢出:
但在有时候,会出现两个字符串不相同但是整数相同的情况,此时我们把“进制”取为131/1331/13331...,模数一般取为
核心代码:
1.准备工作
p[0]=1; for(int i=1;i<maxn;i++) { p[i]=p[i-1]*P; } for(int i=1;i<=n;i++) { h[i]=h[i-1]*P+(ull)s[i]-'a'; }
2.求l,r区间的哈希值
ull gethash(int l,int r) { return h[r]-h[l-1]*p[r-l+1]; }
注意,以上字符串是从1开始的。
2.KMP
显然,我还没有学懂
upd on:2024/7/2
这个算法主要解决的是字符串中关键字搜索。
暴力:从左到右一个一个匹配,时间复杂度为
KMP算法:利用已经部分匹配的有效信息,只修改模式串的指针,让模式串尽量移动到有效位置。
由于我没有图并且自己也不想画,下面的请自行脑补,见谅。
会发现在主串(i 指针)和模式串(j 指针)不匹配的情况下
数学公式表达:
定义:
当
//以下是处理next数组 void getnext(string s,int nxt[]) { int n=s.size(); int j=0; nxt[0]=0; for(int i=1;i<n;i++) { while(j>0&&s[i]!=s[j]) { j=nxt[j-1]; } if(s[i]==s[j]) j++; nxt[i]=j; } } //以下是匹配 void kmp(string s,string t) { int n=s.size(), m=t.size(); for(int i=0,j=0;i<n;i++) { while(j>0&&s[i]!=t[j]) j=nxt[j-1]; if(s[i]==t[j]) j++; if(j==m) { j=nxt[j-1]; //i就是t串末尾 } } }
3.trie树
差不多就是把一些字符串转化为一棵树
是一种用于快速查询某个字符串/字符前缀是否存在的数据结构。
插入/生成
//idx代表当前字符的编号,根节点为0 //son数组一维下标是父节点的idx,二维下标是这个父节点的直接子节点的str[i]-'a'的值 //cnt数组表示以该idx结尾的字符串的个数,例如:有几个'abc'的字符串 void insert(char str[]) { int p=0; for(int i=0;str[i];i++) { int u=str[i]-'a'; if(!son[p][u]) son[p][u]=++idx; p=son[p][u]; } cnt[p]++; }
查询
int query(char str[])//查询字符串出现的次数 { int p=0; for(int i=0;str[i];i++) { int u=str[i]-'a'; if(!son[p][u]) return 0; p=son[p][u]; } return cnt[p]; }
例如:
关于trie树,第2,3两道题,推荐这篇题解 》》》( ⊙ o ⊙ )啊!
4.AC自动机
由于前面学的知识都忘了,一整个复习+回顾代码,发现很难肝,顺便修整了博客(
#include<bits/stdc++.h> using namespace std; const int N=5e5+5, M=1e6+5; int n, son[N][26], cnt[N], idx, T; char s[100], s1[M]; int fail[N];//失配指针 void insert(char str[]) { int p=0; for(int i=0;str[i];i++) { int u=str[i]-'a'; if(!son[p][u]) son[p][u]=++idx; p=son[p][u]; } cnt[p]++; } void build()//建AC自动机 { queue<int> q; for(int i=0;i<26;i++) { if(son[0][i]) q.push(son[0][i]); } while(q.size()) { int u=q.front();//用于BFS遍历字典树 q.pop(); for(int i=0;i<26;i++) { int v=son[u][i]; if(v) //存在 { fail[v]=son[fail[u]][i];//失配指针就是父节点的失配指针的同样的儿子 q.push(v); } else son[u][i]=son[fail[u]][i];//一种特殊处理,很像并查集里的路径压缩 } } } int query(char str[]) { int ans=0, u=0; for(int i=0;str[i];i++) { u=son[u][str[i]-'a']; for(int j=u;j&&cnt[j]!=-1;j=fail[j]) { ans+=cnt[j], cnt[j]=-1;//防止重复计算 } } return ans; } int main() { cin>>T; while(T--) { memset(fail, 0, sizeof(fail)); memset(son, 0, sizeof(son)); memset(cnt, 0, sizeof(cnt)); cin>>n; for(int i=0;i<n;i++) { cin>>s; insert(s); } cin>>s1; build(); cout<<query(s1)<<endl; } return 0; }
即使所有人都放弃你,仍有几十亿细胞为你而活。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效