2023-01-30 14:32阅读: 38评论: 0推荐: 1

字符串

Trie

简介

n 个字符串存储在一棵树中。每个字符代表一条边,根节点到每个单词节点经过的所有边代表了所有的字符串。

例题

P8306【模板】字典树

分析

字典树模板,建个字典树然后查询,注意要把数字和字母转换为数字下标。

代码

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 3e6 + 10;
int T, n, q;
char c[N];
int trie[N][65], tot, cnt[N];

int get(char c){
	if (c >= 'a' && c <= 'z') return c - 'a';
	if (c >= 'A' && c <= 'Z') return c - 'A' + 26;
	if (c >= '0' && c <= '9') return c - '0' + 52;
}

void insert(char *c){
	int sz = strlen(c + 1), now = 0;
	for (int i = 1; i <= sz; i++){
		int &to = trie[now][get(c[i])];
		if (!to) to = ++tot;
		now = to;
		cnt[now]++;
	}
}

int find(char *c){
	int sz = strlen(c + 1), now = 0;
	for (int i = 1; i <= sz; i++){
		int to = trie[now][get(c[i])];
		if (!to) return 0;
		now = to;
	}
	return cnt[now];
}

int main(){
	T = read();
	while (T--){
		for (int i = 0; i <= tot; i++){
			for (int j = 0; j < 62; j++){
				trie[i][j] = 0;
			}
			cnt[i] = 0;
		}
		tot = 0;
		n = read(), q = read();
		for (int i = 1; i <= n; i++){
			cin >> c + 1;
			insert(c);
		}
		for (int i = 1; i <= q; i++){
			cin >> c + 1;
			printf("%d\n", find(c));
		}
	}
	return 0;
} 

P4471 [BJWC2018]词韵

分析

显然易见,我们可以反着建一遍边,则题目转换为最长公共前缀,即可发现,两个字符串的结尾处,要么是兄弟,要么是父子关系,然后考虑树形 dp
对于每一个点,我们设 f[i] 为当前最长单词序列的长度,word[i] 为是否有单词已这个节点结尾。显然如果 word[i]=0f[i]=0
继续考虑,我们发现 f[i] 加上自己儿子中 f[son] 最大的值的儿子一定最优,由于儿子和自己也可以加上,则将剩余儿子个数和自己也加上。
最后考虑答案统计,肯定是由一个最长链和一个次长链与剩余儿子个数加上自己。

代码

#include<iostream>
#include<cstring>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 3e6 + 10;
int n, ans;
char c[N];
int trie[N][27], tot;
bool word[N];
int f[N];

void insert(char *c){
	int sz = strlen(c + 1), now = 0;
	for (int i = sz; i >= 1; i--){
		int &to = trie[now][c[i] - 'a'];
		if (!to) to = ++tot;
		now = to;
	}
	word[now] = 1;
}

void dfs(int u){
	int maxx = 0, maxn = 0, child = 0;
	for (int i = 0; i < 26; i++){
		int v = trie[u][i];
		if (!v) continue;
		child += word[v];
		dfs(v);
		if (f[v] > maxx) maxn = maxx, maxx = f[v];
		else maxn = max(maxn, f[v]);
	}
	ans = max(ans, maxx + maxn + word[u] + max(0, child - 2));
	if (!word[u]){
		f[u] = 0;
		return;
	}
	f[u] = maxx + word[u] + max(0, child - 1);
}

int main(){
	n = read();
	for (int i = 1; i <= n; i++){
		cin >> c + 1;
		insert(c);
	}
	dfs(0);
	printf("%d\n", ans);
	return 0;
}

P4551 最长异或路径

分析

可以考虑将每个点到根节点的异或值求出来(默认根节点为 1)。然后可以发现,两个点到根节点的异或值就是两个节点路径上的异或值。然后考虑将每个点到根的异或值放到 01trie 上,然后枚举异或值贪心求解。贪心求解就是如果遇到这一位与另一位不同就从这一条搜下去,否则就按照一条搜下去。

代码

#include<iostream>
#include<cstdio>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 1e5 + 10, M = 3e6 + 10;
int n, ans;
struct tree{
	int v, w, nxt;
}t[N << 1];
int head[N], cnt;

void add(int u, int v, int w){
	t[++cnt] = (tree){v, w, head[u]};
	head[u] = cnt;
}

int sum[N];
void dfs(int u, int fa){
	for (int i = head[u]; i; i = t[i].nxt){
		int v = t[i].v;
		if (v == fa) continue;
		sum[v] = sum[u] ^ t[i].w;
		dfs(v, u);
	}
}

int trie[M][2], tot;
void insert(int s){
	int now = 0;
	for (int i = 1 << 30; i; i >>= 1){
		bool v = s & i;
		int &to = trie[now][v];
		if (!to) to = ++tot;
		now = to;
	}
}

int find(int s){
	int sum = 0, now = 0;
	for (int i = 1 << 30; i; i >>= 1){
		bool v = s & i;
		int to = trie[now][!v];
		if (to) sum += i, now = to;
		else now = trie[now][v];
	}
	return sum;
}

int main(){
	n = read();
	for (int i = 1; i < n; i++){
		int u = read(), v = read(), w = read();
		add(u, v, w);
		add(v, u, w);
	}
	dfs(1, 0);
	for (int i = 1; i <= n; i++){
		insert(sum[i]);
	}
	for (int i = 1; i <= n; i++){
		ans = max(ans, find(sum[i]));
	}
	printf("%d\n", ans);
	return 0;
} 

KMP

AC自动机

简介

多字符串匹配的算法。

例题

P3808 【模板】AC 自动机(简单版)

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 1e6 + 10;
int n;
char s[N];

namespace AC{
	int trie[N][27], word[N], tot;
	void insert(char *c){
		int sz = strlen(c + 1), now = 0;
		for (int i = 1; i <= sz; i++){
			int &to = trie[now][c[i] - 'a'];
			if (!to) to = ++tot;
			now = to;
		}
		word[now]++;
	}
	int fail[N];
	queue<int> q;
	void build(){
		for (int i = 0; i < 26; i++){
			if (trie[0][i]) q.push(trie[0][i]);
		}
		while (!q.empty()){
			int u = q.front();q.pop();
			for (int i = 0; i < 26; i++){
				if (trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], q.push(trie[u][i]);
				else trie[u][i] = trie[fail[u]][i];
			}
		}
	}
	int query(char *t){
		int sz = strlen(t + 1), now = 0, sum = 0;
		for (int i = 1; i <= sz; i++){
			int to = trie[now][t[i] - 'a'];
			for (int j = to; j && word[j] != -1; j = fail[j]){
				sum += word[j], word[j] = -1;
			}
			now = to;
		}
		return sum;
	}
}using namespace AC;

int main(){
	n = read();
	for (int i = 1; i <= n; i++){
		cin >> s + 1;
		insert(s);
	}
	cin >> s + 1;
	build();
	printf("%d\n", query(s));
	return 0;
} 

P3796 【模板】AC 自动机(加强版)

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 160, SZ = N * 80, M = 1e6 + 10, P = 80;
int n;
char s[N][P], t[M];

namespace AC{
	int trie[SZ][27], idx[SZ], tot;
	void insert(char *c, int id){
		int sz = strlen(c + 1), now = 0;
		for (int i = 1; i <= sz; i++){
			int &to = trie[now][c[i] - 'a'];
			if (!to) to = ++tot;
			now = to;
		}
		idx[now] = id;
	}
	int fail[SZ];
	queue<int> q;
	void build(){
		for (int i = 0; i < 26; i++){
			if (trie[0][i]) q.push(trie[0][i]);
		}
		while (!q.empty()){
			int u = q.front();q.pop();
			for (int i = 0; i < 26; i++){
				if (trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], q.push(trie[u][i]);
				else trie[u][i] = trie[fail[u]][i];
			}
		}
	}
	int val[SZ], cnt[SZ];
	int query(char *t){
		int sz = strlen(t + 1), now = 0, sum = 0;
		for (int i = 1; i <= sz; i++){
			int to = trie[now][t[i] - 'a'];
			for (int j = to; j; j = fail[j]) val[j]++;
			now = to;
		}
		for (int i = 0; i <= tot; i++){
			if (idx[i]) sum = max(sum, val[i]), cnt[idx[i]] = val[i];
		}
		return sum;
	}
	void init(){
		memset(trie, 0, sizeof trie);
		memset(idx, 0, sizeof idx);
		memset(fail, 0, sizeof fail);
		while (!q.empty()) q.pop();
		memset(val, 0, sizeof val);
		memset(cnt, 0, sizeof cnt);
		tot = 0;
	}
}using namespace AC;

int main(){
	while (~scanf("%d", &n)){
		init();
		if (!n) break;
		for (int i = 1; i <= n; i++){
			cin >> s[i] + 1;
			insert(s[i], i);
		}
		cin >> t + 1;
		build();
		int ans = query(t);
		printf("%d\n", ans);
		for (int i = 1; i <= n; i++){
			if (cnt[i] == ans) printf("%s\n", s[i] + 1);
		}
	}
	return 0;
}

P5357 【模板】AC 自动机(二次加强版)

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 2e5 + 10, M = 2e6 + 10;
int n;
char s[N], t[M];

namespace AC{
	struct edge{
		int v, nxt;
	}e[N];
	int head[N], cnt;
	void add(int u, int v){
		e[++cnt] = (edge){v, head[u]};
		head[u] = cnt;
	}
	int trie[N][27], idx[N], tot;
	void insert(char *c, int id){
		int sz = strlen(c + 1), now = 0;
		for (int i = 1; i <= sz; i++){
			int &to = trie[now][c[i] - 'a'];
			if (!to) to = ++tot;
			now = to;
		}
		idx[id] = now;
	}
	int fail[N];
	queue<int> q;
	void build(){
		for (int i = 0; i < 26; i++){
			if (trie[0][i]) q.push(trie[0][i]);
		}
		while (!q.empty()){
			int u = q.front();q.pop();
			for (int i = 0; i < 26; i++){
				if (trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], q.push(trie[u][i]);
				else trie[u][i] = trie[fail[u]][i];
			}
		}
		for (int i = 1; i <= tot; i++){
			add(fail[i], i);
		}
	}
	int val[N];
	void query(char *t){
		int sz = strlen(t + 1), now = 0;
		for (int i = 1; i <= sz; i++){
			int to = trie[now][t[i] - 'a'];
			val[to]++;
			now = to;
		}
	}
	int ans[N];
	void dfs(int u){
		ans[u] = val[u];
		for (int i = head[u]; i; i = e[i].nxt){
			int v = e[i].v;
			dfs(v);
			ans[u] += ans[v];
		}
	}
}using namespace AC;

int main(){
	n = read();
	for (int i = 1; i <= n; i++){
		cin >> s + 1;
		insert(s, i);
	}
	cin >> t + 1;
	build();
	query(t);
	dfs(0);
	for (int i = 1; i <= n; i++){
		printf("%d\n", ans[idx[i]]);
	}
	return 0;
} 

本文作者:bryce_yyds

本文链接:https://www.cnblogs.com/bryceyyds/p/17075852.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   bryce_yyds  阅读(38)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起