来自 szc 的字符串和搜索的总结
膜拜 szc 大佬。
原链接。
哈希
普通哈希不讲了,讲讲树哈希。
对于判断一对同构树,要考虑相同结构的儿子在两类树的不同位置。
此时有两种方法,一种是正常的按序哈希,我们很好想到在哈希时对儿子节点的哈希值进行排序,规定一个顺序塞进去。
另一种方法则是不使用多项式哈希,对所有哈希值在不乘上模数的情况下进行求和,这样可以保证其哈希值是顺序无关的,而且好回溯。
为了防卡,此时我们就需要改变一下哈希函数的式子,如变成异或哈希。
哈希表
总有题目用的到哈希表的。
这里讲一下对于一个哈希表的冲突,我们通常使用散列表的方式(应该是叫这个),也叫拉链法。
就是把哈希值取个模,对于取模后的哈希值当做起点,造出一个连向该哈希值的边,对于查询时遍历该哈希值的模数后判断是否存在就行了。
这里是单哈希的板子(未实战慎复制)
struct HashTable{ struct edge{ struct node { ull w; int nxt,to; node(int x=0,ull y=0,int z=0){ to=x,w=y,nxt=z; } }e[N]; int head[mod+1],tot; inline void add(int u,int v,ull w){ e[++tot]=node(v,w,head[u]),head[u]=tot; } inline node&operator[](int x){return e[x];} }e; inline int get(ull key) { return (key%mod+mod)%mod; } int query(ull a){ for(int i=e.head[get(a)];i;i=e[i].nxt) if(e[i].w==a) return e[i].to; return -1; } inline void insert(ull a,int b){ int tmp=get(a); e.add(tmp,a,b); } }fyn;
KMP
KMP 常见于字符串匹配,简单复习一下。
定义
对于新增节点
1234....f[i-1],x,...,(i-1)-f[i-1]+1,...,i-1,i
其中可以保证
若是相同的则
若是不相同,我们就令失配指针跳到
1234...f[f[i-1]],x,...f[i-1]-f[f[i-1]]+1,...,f[i-1],...,i
显然我们再对
kmp 例题就不放了,直接讲讲 kmp 套 dp 以及失配树。
以及 失配树
扩展KMP(Z函数)
考虑
考虑如何求得
首先朴素算法,我们从
显然我们能优化的只有暴力向后扫的这个循环,考虑如何将
我们记
-
若
,则我们可以知道此时的 是处于该匹配串中的,而 是无法匹配的,因此我们可以得出 -
否则我们以
为左端点再往右跑朴素算法。
这样子最终也是
Manacher
首先考虑最朴素的回文串算法,暴力枚举中心点然后向左右拓展到无法添加为止,复杂度
首先回文串需要区分为奇数串和偶数串,而我们通过一个小技巧将其合并为一种情况。
我们首先往串中穿插 '#'
,构造出一个
对于在 #
,而奇数回文串必然以原字符串中的字符为中心,偶数串则是以我们穿插进去的 '#' 为中心。
考虑上一次计算后最大
考虑新加入的
-
若
则说明当前中心在回文串之外,我们使用朴素算法暴力向外拓展并更新 。 -
若
,则说明 处在我们当前的回文串中,其必然有一个对称点 ,且可以保证以 为中心至少存在一个长度与以 为中心的回文串相同的回文串。
显然此时我们还需要考虑若以
此后记得更新
Trie
Trie,字典树,顾名思义,字典树,一对多或许可以试试。
当然也可以用来求异或和最大。
P8511 [Ynoi Easy Round 2021] TEST
A*,IDA*
首先,了解一下这两个东西。
不要以为这是一个很高深的东西,实际上它是一个不那么高深的东西。
事实上就是一个加了优化的暴力。
A* 实际上就是加了估价函数的 bfs,最经典的就是求 K 短路。
对于每次拓展都贪心地选择我们从开始到当前点的花费加上我们预估接下来的花费的和最小的那个点,并进行拓展。
P2901 [USACO08MAR] Cow Jogging G
IDA* 实际上就是加了估价函数和迭代加深 (没有tim<=1e6) 的dfs,一般适用于拥有限制 (可以不可以总司令) 的题目。
然而这里的选择就有些不同,对于一个状态我们需要预估出该状态在最优情况下到达最终态的步数,及能走的最小步数,然后判断其加上当前深度是否超出迭代的最大深度,若超出则返回。
其实就是剪枝。
以及 P3179 八数码
贴个八数码的 IDA*
#include<bits/stdc++.h> using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); auto _u=[](char x){return x>='0'&&x<='9';}; for(;!_u(ch);ch=='-'&&(f=-1),ch=getchar()); for(;_u(ch);x=(x<<1)+(x<<3)+(ch^48),ch=getchar()); return x*f; } inline void write(int num,bool flag=0){ static int st[39],tp=0; num<0&&(putchar('-'),num=-num); do st[++tp]=num%10;while(num/=10); while(tp) putchar(st[tp--]|48); putchar(flag?'\n':' '); return; } template<typename...Args> inline void write(int t,Args...args){ return write(t),write(args...); } short mp[3][3]={ {1,2,3}, {8,0,4}, {7,6,5} }; short a[3][3]; inline int check(){ int cnt=0; for(int i=0;i<3;++i) for(int j=0;j<3;++j) cnt+=(a[i][j]!=mp[i][j]); return cnt; } int d[4][2]={{0,1},{1,0},{-1,0},{0,-1}}; inline bool in(int x,int y){ return x>=0&&x<3&&y>=0&&y<3; } bool dfs(int x,int y,int dep,int lst,int mxdep){ if(dep==mxdep)return check()==0; for(int i=0,tx,ty;i<4;++i) if(in(tx=x+d[i][0],ty=y+d[i][1])&&lst+i!=3){ swap(a[tx][ty],a[x][y]); if(check()+dep<=mxdep&&dfs(tx,ty,dep+1,i,mxdep)) return true; swap(a[tx][ty],a[x][y]); } return false; } inline void A_star(int x,int y){ if(check()==0)puts("0"); else for(int i=1;;++i) if(dfs(x,y,0,-1,i)) return write(i,true); } char s[10]; signed main(){ scanf("%s",s); int x,y; for(int i=0;i<9;++i) if(!(a[i/3][i%3]=s[i]^48)) x=i/3,y=i%3;; A_star(x,y); return(0-0); }
~
折半搜索,就是折一半搜索。
将原本的一组数据分组爆搜,再用一种时间复杂度较低的方法将各组数据合并为最终答案。
显然由此可得,我们不仅需要对可以暴力的数据范围敏感,还要对其倍数敏感。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!