链接
https://vjudge.net/problem/UVA-1519
题解
首先这题很显然和后缀数组扯不上关系,暴力又想不出来,所以感觉就是字典树或者AC自动机题
考虑把所有串建成字典树
那么字典树上每一个结点代表一个前缀,遍历字典树就可以做到不重复不遗漏地枚举所有前缀
现在考虑在每个前缀上连接后缀
首先我先连接上所有的后缀,然后考虑减去那些重复统计的
如果一个后缀,它在单词种的前一个字母和当前前缀的最后一个字母相同,那么在当前前缀所代表结点的父亲节点就已经统计过这个串,所以应当减去这样的串
把所有串反转建字典树,然后统计每个字母的出现次数即可解决这个问题
代码
#include <bits/stdc++.h>
#define maxn 400010
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
struct Trie
{
int trie[maxn][27], tot, fail[maxn], tail[maxn], alpha[maxn], depth[maxn];
void clear() //clear the arrays
{
for(int i=1;i<=tot;i++)cl(trie[i]), fail[i]=tail[i]=depth[i];
tot=1;
}
int insert(int *r, int len) //insert a string into trie tree
{
auto pos=1;
for(auto i=1;i<=len;i++)
pos = trie[pos][r[i]] ? trie[pos][r[i]] : trie[pos][r[i]]=++tot;
tail[pos]++;
return pos;
}
void dfs(int pos)
{
for(auto i=1;i<=26;i++)
{
if(trie[pos][i])
{
auto v(trie[pos][i]);
depth[v]=depth[pos]+1;
alpha[v]=i;
dfs(v);
}
}
}
}pre, suf;
char s[45];
int r[45], cnt[30], vis[30];
int main()
{
freopen("in.txt","r",stdin);
int n, len, i, j;
ll ans;
while(~scanf("%d",&n))
{
pre.clear();
suf.clear();
cl(cnt);
cl(vis);
ans=0;
for(i=1;i<=n;i++)
{
scanf("%s",s+1);
len=strlen(s+1);
if(len==1)
{
vis[s[1]-'a'+1]++;
if(vis[s[1]-'a'+1]==1)ans++;
}
for(j=1;j<=len;j++)r[j]=s[j]-'a'+1;
pre.insert(r,len);
reverse(r+1,r+len+1);
suf.insert(r,len);
}
pre.dfs(1);
suf.dfs(1);
for(auto i=1;i<=suf.tot;i++)
{
if(suf.depth[i]>1)cnt[suf.alpha[i]]++;
}
for(auto i=2;i<=pre.tot;i++)
{
ans+=suf.tot-1;
if(pre.depth[i]>1)ans-=cnt[pre.alpha[i]];
}
printf("%lld\n",ans);
}
return 0;
}