题意:有一个字典,字典有很多单词,要求组合成新词,新词必须来源于原字典,或者由原词的非空前缀和非空后缀组成。
思路:建立trie树,则可以求出不同的前缀和后缀有多少个,但是直接相乘并不是答案,考虑有可能重复的,如XXXAXXX,则A可以选前缀和后缀各一次,XXXA、XXX或者XXX、XXXA,则应该减去。
现在考虑普遍情况,XXXbbb,bbbXXX,若只考虑重复b的情况,假设左边x个b,右边y个b,不考虑重复情况左边应为(1+x)选择,右边应为(1+y)选择,则总共应有这么(1+x)*(1+y)多种情况,但是实际情况应该只有1+x+y种情况,(b的数量,X为任意字符),则应该减去x*y种情况。
还有一个要注意的是如果出现单独的字符,应该标记,建立trie树的时候,不会把单个字符算进去。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mem(name,value) memset(name,value,sizeof(name))
#define FOR(i,n) for(int i=1;i<=n;i++)
using namespace std;
const int maxn = 400000+5;
const int sigma_size = 26;
struct Trie{
int ch[maxn][sigma_size];
int cnt[sigma_size];
int sz;
int init(){
mem(ch[0],0); mem(cnt,0);
sz = 1;
}
int idx(char c){return c-'a';}
void insert(const char *s){
int n = strlen(s),u=0;
for(int i=0;i<n;i++){
int c = idx(s[i]);
if(!ch[u][c]){
mem(ch[sz],0);
ch[u][c] = sz++;
if(i) cnt[c]++;
}
u = ch[u][c];
}
}
}pre,suf;
char s[100];
bool vis[26];
int main(){
//freopen("in.txt","r",stdin);
int n;
while(scanf("%d",&n)==1){
mem(vis,false);
pre.init(); suf.init();
for(int i=0;i<n;i++){
scanf("%s",s);
if(strlen(s)==1) vis[s[0]-'a']=1;
pre.insert(s);
reverse(s,s+strlen(s));
suf.insert(s);
}
long long ans = (long long)(pre.sz-1)*(suf.sz-1);
for(int i=0;i<sigma_size;i++) ans -= (long long)pre.cnt[i]*suf.cnt[i];
for(int i=0;i<sigma_size;i++) if(vis[i]) ans++;
printf("%lld\n",ans);
}
return 0;
}