非线性字符串数据结构串讲

书接去年,今天作业不想写了,滚过来写总结。顺便保留我刚略微学会的串串。

声明:作者由于水平不高,所以有些定理不能严谨证明,所以若是初学者请移步别处。

1.Trie树

定义

Trie树又叫字典树,是非常显然的一种字符串数据结构。

具体来说,我们如果手上有很多字符串,我们可以通过建一个Trie树来做一些简单的操作。

其构建过程可以看一下图,非常显然,本质就是把字符串的相同部分压缩一下。

比如我们对

复制代码
aa

aba

ba

caaa

cab

cba

cc

建一个Trie就长这样。

构建

图来自OI wiki

然后构建这块也挺简单的

代码

cpp 复制代码
int len,idx,cnt[N],tr[N][70];
ll getid(char c){
	if(c>='A'&&c<='Z') return c-'A';
	else if(c>='a'&&c<='z') return c-'a'+26;
	else return c-'0'+52;
}
void add(string s){
	len=s.size();
	s=' '+s;
	int p=0;
	for(int i=1;i<=len;i++){
		int id=getid(s[i]);
		if(!tr[p][id]) tr[p][id]=++idx;
		p=tr[p][id];
		cnt[p]++;
	}
}
ll ask(string s){
	len=s.size();
	s=' '+s;
	int p=0;
	for(int i=1;i<=len;i++){
		int id=getid(s[i]);
		if(!tr[p][id]) return 0;
		p=tr[p][id];
	}
	return cnt[p];
}

空间这块,最坏情况每个字符串的每个字符都要新开一个节点,所以空间开即可。

上一道例题

AT_arc219_a ARC219A Similarity

AT_arc219_a ARC219A Similarity

正难则反

这个比较困难

所以就把S串01翻转,那么就是找出一个串串使得不能与任何一个S串相同。

那你给这些反串建出Trie后,找一个还没有被经过的节点即可。

例题结束

与异或相关

其实我们的Trie树不只能在字符串上用

因为在我们眼里数是有二进制的,二进制我们就可以想到01串。

我们就可以把数字当做01串建Trie树

当我们遇到一些神秘有关异或的题目时,

我们一想Trie树,二想线性基

比如来道题,给定你一个序列和x值,让你从这个序列中选一个元素,使之其与x的异或值最大。

考虑异或的性质,二进制位上不同为一,相同为0。

所以我们对序列建Trie树,把给定的x二进制分解,从高往低位的贪心的尽可能与x当前位不同。就行了。

来一道有意思的应用

P5283 十二省联考 2019 异或粽子

题解

2.AC自动机

定义

AC自动机,由两部分构成Trie树和fail树。

AC自动机的本质上就是建出Trie树后连fail边。

这里我们给出fail边的定义。

在Trie树上,每一个节点表示一个字符串。

fail边指向这个节点所代表的字符串 最长的后缀 所代表的节点。

图例

example:

图来自OI wiki

比如这个Trie树

给他建fail边就应该是这样的

由于根节点代表的是空串,空串是任何字符串的后缀。

所以当你实在在Trie树上找不到后缀时,就直接无脑连根结点就可以啦。

然后AC自动机的功能这块,可以通过拿一个字符串在上面跑得到在自动机上他的所有子串,不管通过Trie边还是fail边。

代码

cpp 复制代码
int n,m,len,idx=1;
string s;
struct trie{
	int son[29],fail;
	int flag;
}t[N];
struct AC_automation{
	void build(string s){
		len=s.size(); s=' '+s;
		int p=0;
		for(int i=1;i<=len;i++){
			int v=s[i]-'a'+1;
			if(!t[p].son[v]) t[p].son[v]=++idx;
			p=t[p].son[v];
		}
		t[p].flag++;
	}
	void getfail(){
		queue<int> q;
		for(int i=1;i<=26;i++) if(t[0].son[i]) q.push(t[0].son[i]);
		while(!q.empty()){
			int p=q.front();
			q.pop();
			for(int i=1;i<=26;i++){
				int v=t[p].son[i];
				int faf=t[p].fail;
				if(!v){
					t[p].son[i]=t[faf].son[i];
					continue;
				}
				t[v].fail=t[faf].son[i];
				q.push(v);
			}
		}
	}
	ll ask(string s){
		len=s.size(); s=' '+s;
		int p=0; ll ans=0;
		for(int i=1;i<=len;i++){
			int v=s[i]-'a'+1;
			int k=t[p].son[v];
			while(k>1&&t[k].flag!=-1){
				ans+=t[k].flag,t[k].flag=-1;
				k=t[k].fail;
			}
			p=t[p].son[v];
		}
		return ans;
	}
	
}AC;

P9613 CERC2019 K==S

大意就是构造一个长度为N的串串,让你是这个串串不包含给出的禁用短语作为子串,求方案数。

多模匹配想到建AC自动机并建图,由于不能含某些子串,所以不能走到这些子串的节点以及能通过跳fail边到这些子串的地方。

发现N很大,直接DP不行。但是状态转移固定,所以使用矩阵乘法优化DP就好了。

3.后缀数组

其实有点跑题了,因为后缀数组是线性的,但是后缀数组比较困难所以就写在这里了

定义

性质

图文来自洛谷

代码

cpp 复制代码
string s;
int sa[N],c[N],x[N],y[N],height[N],rk[N];
namespace SA{
	void getsa(){
		for(int i=1;i<=n;i++)++c[x[i]=s[i]];
		for(int i=2;i<=m;i++)c[i]+=c[i-1];
		for(int i=n;i;i--)sa[c[x[i]]--]=i;
		for(int k=1;k<=n;k*=2){
			int num=0;
			for(int i=n-k+1;i<=n;i++)y[++num]=i;
			for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
			for(int i=1;i<=m;i++)c[i]=0;
			for(int i=1;i<=n;i++)++c[x[i]];
			for(int i=2;i<=m;i++)c[i]+=c[i-1];
			for(int i=n;i;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
			swap(x,y);
			x[sa[1]]=1;
			num=1;
			for(int i=2;i<=n;i++){
				x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
			}
			if(num==n)break;
			m=num;
		}
	}
	void getheight(){
		for(int i=1;i<=n;i++) rk[sa[i]]=i;
		int k=0;
		for(int i=1;i<=n;i++){
			if(rk[i]==1) continue;
			if(k) k--;
			int j=sa[rk[i]-1];
			while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
			height[rk[i]]=k;
		}
	}
}
using namespace SA;

4.后缀自动机

相关推荐
库玛西34 分钟前
现代 C++ 智能指针全景指南:从 RAII 思想到工业级实践
c语言·开发语言·c++·笔记·面试
liulilittle1 小时前
无锁并发容器的设计与实现原理
开发语言·c++·set·map·并发·无锁·lock-free
还不秃顶的计科生1 小时前
具身智能论文学习8:Octo: An Open-Source Generalist Robot Policy
人工智能·深度学习·学习·机器学习·语言模型·vla·vlm
woshihuanglaoshi2 小时前
事务加持防超卖:ArkTS 在鸿蒙里玩转 TRANSACTION 出入库
学习·华为·harmonyos
qeen872 小时前
【数据结构】自平衡二叉搜索树各种旋转算法原理解析及AVL树的C++实现
数据结构·c++·算法
世人万千丶2 小时前
去重插入与超限淘汰:ArkTS 实现鸿蒙搜索历史的 LIMIT 艺术
运维·服务器·学习·华为·harmonyos·鸿蒙
lucas_AI2 小时前
1.2B 小模型赢过 235B 大模型:NaviDC-OCR 把文档解析卷明白了
人工智能·深度学习·算法
Augustzero2 小时前
为什么线程不能说睡就睡?看懂等待与唤醒机制
c++·后端
程序员与背包客_CoderZ2 小时前
Linux D-Bus通信协议详解:从入门到C/C++编码实战
linux·服务器·c语言·c++·嵌入式硬件·嵌入式软件·dbus
冻柠檬飞冰走茶3 小时前
PTA基础编程题目集 7-35有理数均值(C++语言实现)
开发语言·数据结构·c++·算法·均值算法