专栏文章

2024.12.21

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miqpsrgf
此快照首次捕获于
2025/12/04 08:45
3 个月前
此快照最后确认于
2025/12/04 08:45
3 个月前
查看原文

重生之我与字符串的殊死搏斗

wangzilong913: 该死的字符串,纳命来!!
string: 就凭你?吃我一招最小表示法橫劈!!

一.最小表示法

1.基本概念:

给定一个字符串,如果我们不断把它的最后一个字符放到开头,最终得到 nn 个字符串,我们称这 nn 个字符串是循环重构的。这些字符串中字典序最小的一个,称为字符串 SS 的最小表示。
(字典序:在比较字典序中,首先比较两个字符串的第一个字符,如果不同,则第一个字符较小的字符串更小;如果相同,则继续比较下一个字符,依此类推,直到比较完所有字符。如果一个字符串是另一个字符串的前缀,则较短的字符串更小。)

2.基本原理:

首先把 SS 复制一份接在它的结尾,得到字符串 SSSS 。显然,以 ii 开始的循环同构字符串 B[iB[i ~ i+n1]=SS[ii+n-1] = SS[i ~ i+n1i+n-1
如果在 i+ki+kj+kj+k 处发现不相等,假设 SS[i+k]>SS[j+k]SS[i+k] > SS[j+k] ,那么我们当然可以得知 B[i]B[i] 不是 SS 的最小表示(因为存在一个更小的循环同构串 B[j]B[j] )。除此之外,我们还可以得知 B[i+1],B[i+2]......,B[i+k]B[i+1],B[i+2]......,B[i+k] 也都不是 SS 的最小表示。这是因为对于 1<=p<=k1<=p<=k ,存在一个比 B[i+p]B[i+p] 更小的循环同构串 B[j+p]B[j+p] (从 i+pi+pj+pj+p 开始扫描,同样会在 p=kp=k 时发现不相等,并且 SS[j+p]>SS[j+k]SS[j+p]>SS[j+k])。
同理,如果 SS[i+k]<SS[j+k]SS[i+k]<SS[j+k] ,那么 B[j+1],B[j+2]......,B[j+k]B[j+1],B[j+2]......,B[j+k] 都不是 SS 的最小表示,直接跳过这些位置,一定不会遗漏最小表示。
因此,我们可以做到用 O(n)O(n) 的时间复杂度求出字符串的最小表示。

3.模板:

注:原题是整数的最小表示,此处写的是字符串
CPP
#include <bits/stdc++.h>
using namespace std;
int n,ans;
char a[6000010];
int main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
		a[i+n] = a[i];
	}
	int i = 1,j = 2,k;
	while(i <= n && j <= n){
		for(k = 0;k < n && a[i+k] == a[j+k];k++);
		if(k == n) break;
		if(a[i+k] > a[j+k]){
			i = i + k + 1;
			if(i == j) i++;
		}
		else{
			j = j + k + 1;
			if(i == j) j++;
		}
	}
	ans = min(i,j);
	for(int i = ans;i < ans+n;i++){
		cout<<a[i]<<" ";
	}
	return 0;
} 
string: 居然抗住了这一刀,看来是小瞧你了。
wangzilong: 代码量又小,理解也不难,你看这string就是逊啊!!
string: 哟,这么说你很勇咯。区区蝼蚁,也妄想挑战我。看我这招Trie树诛仙锁!!

二.Trie树

1.基本概念:

Trie树,又叫字典树、前缀树(Prefix Tree)、单词查找树 或 键树,是一种多叉树结构。
Trie树的基本性质:
  1. 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  2. 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
  3. 每个节点的所有子节点包含的字符互不相同。

2.实现

插入:

当需要插入一个字符串 SS 时,我们令一个指针 PP 起初指向根节点。然后依次扫描 SS 中的每一个字符 cc
  1. PPcc 字符指向一个已经存在的节点 QQ ,则令 P=QP=Q
  2. PPcc 字符指向空,则新建一个节点 QQ ,令 PPcc 字符指针指向 QQ ,然后令 P=QP=Q
SS 中的字符扫描完毕时,在当前节点 PP 上标记它是一个字符串的末尾。

检索:

当需要检索一个字符串 SS 在Trie树中是否存在时,我们令一个指针 PP 起初指向根结点,然后依次扫描 SS 中的每个字符 cc
  1. PPcc 字符指向空,则说明 SS 没有被插入过Trie树,结束检索。
  2. PPcc 字符指向一个已经存在的节点 QQ ,则令 P=QP=Q
SS 中的字符扫描完毕时,若当前节点 PP 被标记为一个字符串的末尾,则说明 SS 在Trie树中存在,否则说明 SS 没有被插入过Trie树。

3.模板:

CPP
#include <bits/stdc++.h>
using namespace std;
int n,m;
int t[1000005][26];
int cnt[1000005];
int now;
void cha(string s){
	int p=0;
	for(int i = 0;i < (int)s.size();i++){
		if(t[p][s[i]-'a'] == 0){
			t[p][s[i]-'a'] = ++ now;
		}
		p = t[p][s[i]-'a'];
	}
	cnt[p]++;
}
int zhao(string s){
	int p=0,m=0;
	for(int i = 0;i < (int)s.size();i++){
		if(t[p][s[i]-'a'] == 0){
			return m;
		}
		p = t[p][s[i]-'a'];
		m+=cnt[p];
	}
	return m;
}
int main(){
	cin>>n>>m;
	while(n--){
		string s;
		cin>>s;
		cha(s);
	}	
	while(m--){
		string s;
		cin>>s;
		cout<<zhao(s)<<endl;
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...