uva 1519 - Dictionary Size trie树+简单的数学

利用Trie树解决UVA 1519字典大小问题,通过计算前缀树和后缀树节点数找到可能的组合,注意非空前缀和非空后缀的条件,处理长度为1的特殊情况以避免重复计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接:题目链接

题目大意:给定n个字符串,他们属于一个字典。现在要构造一个新的字典,它要么属于原字典,要么属于原字典的一个非空前缀+源字典的非空后缀。

思路:

首先看到前缀,后缀就应该直接想trie树,trie树正反建树可以知道一共有几个前缀和一共有几个后缀。

假设前缀树有x1个节点,后缀树有x2个节点,那么答案ans=x1*x2.

但是这样显然是重复的,我们看什么情况下会重复:

样例:

s1:abc
s2:def 
s3:abef

取s1前缀ab和s3后缀ef ->abef
取s1前缀a和s3后缀bef ->abef
二者都会被统计到答案中去,但是二者是重复的,只应该被记一次。
所以,我们要分别对两棵树统计每个字母出现的次数,假设对字母b记为cnt1['b'],cnt2['b'];
那么我们的答案还要减去cnt1['b']*cnt2['b'];

统计的时候要注意,题目要求是非空前缀和非空后缀,所以一定要从深度为2的点开始记录,否则会出现:

s1:ab
s2:ac

s1取a s2取c ->ac
s1取空 s2取ac ->ac

这是不合法的

 

还没完。这样我们可以知道 最小统计到的字符串是左边前缀长度为1,右边长度前缀为1,长度最小是2.

但是原字符串中可能存在一些本身长度为1的字符串。

这时候要再标记一下是否有这样的字符串,去重之后加上这些字符串的数量就是最终答案。

 

 

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<ctime>
#define up(i,x,y) for(int i = x;i <= y;i ++)
#define down(i,x,y) for(int i = x;i >= y;i --)
#define mem(a,b) memset((a),(b),sizeof(a))
#define mod(x) ((x)%MOD)
#define lson p<<1
#define rson p<<1|1
using namespace std;
typedef long long ll;
const int SIZE = 500010;
const int INF = 2147483640;
const double eps = 1e-8;

inline void RD(int &x)
{
    x = 0;  char c; c = getchar();
    bool flag = 0;
    if(c == '-')    flag = 1;
    while(c < '0' || c > '9')   {if(c == '-')   {flag = 1;} c = getchar();}
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0',c = getchar();
}

int n;
int trie_pre[SIZE][26],trie_suf[SIZE][26],l,root,tot_pre,tot_suf;
int cnt_pre[SIZE],cnt_suf[SIZE];

void insert_pre(char s[])
{
	root = 0;
	int l = strlen(s);
	for(int i = 0;i < l;i ++)
	{
		int x = s[i] - 'a';
		if(!trie_pre[root][x])
		{
			trie_pre[root][x] = ++tot_pre;
			mem(trie_pre[tot_pre],0);
			if(i)	cnt_pre[x] ++;
		}
		root = trie_pre[root][x];
	}
}

void insert_suf(char s[])
{
	root = 0;
	int l = strlen(s);
	for(int i = 0;i < l;i ++)
	{
		int x = s[i] - 'a';
		if(!trie_suf[root][x])
		{
			trie_suf[root][x] = ++tot_suf;
			mem(trie_suf[tot_suf],0);
			if(i)	cnt_suf[x] ++;
		}
		root = trie_suf[root][x];
	}
}
bool vis[26];
void init()
{
	mem(trie_pre[0],0);
	mem(trie_suf[0],0);
	tot_suf = tot_pre = 0;
	mem(cnt_suf,0);
	mem(cnt_pre,0);
	mem(vis,0);
}
ll ans;
char s[66];


int main(int argc, char const *argv[])
{
	while(~scanf("%d",&n))
	{
		init();
		for(int i = 1;i <= n;i ++)
		{
			scanf("%s",s);
			int sl = strlen(s);
			if(sl == 1)	vis[s[0]-'a'] = 1;
			insert_pre(s);
		//	cout<<s<<endl;
			reverse(s,s+sl);
		//	cout<<s<<endl;
			insert_suf(s);//倒序
		}
		ans = (ll)tot_pre * (ll)tot_suf;
		for(int i = 0;i < 26;i ++)
		{
			ans -= (ll)cnt_pre[i] * (ll)cnt_suf[i];
		//	printf("pair: %d %d\n",cnt_pre[i],cnt_suf[i]);
		//	printf("i:%d ans:%lld\n",i,ans);
			ans += (ll)vis[i];
		}
		printf("%lld\n",ans);
	}
	return 0;
}
/*
3
abc
def
abef
2
abc
c
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值