链接:题目链接
题目大意:给定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
*/