把每个单词正反两次加到字典里,输入每一个单词,每次都会把它输成其他2*n-1个单词,再按退格修改。求总退格数。
http://www.bnuoj.com/bnuoj/problem_show.php?pid=29355
Trie树。先把字符串正反两次加到Trie树里。维护cnt[i], 表示i节点存储相同字母的单词的个数。
sum表示 在没有任何节点重合的情况下节点的总和,也就是长度之和。
在输入一个单词的时候,如果cnt[i] = k,那么在输入这个单词的时候在i节点就少退格k次,在这个节点一共有k个串满足这个要求,所以一共少退格k*k次
算出一共少退格多少次,再用sum减去这个值就是答案。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MaxNode = 500000 + 10;
typedef long long ll;
int chd[MaxNode][26], ID[128];
ll cnt[MaxNode], sz, sum;
char str[MaxNode];
int T, n;
void init(){
memset(chd, 0, sizeof(chd));
memset(cnt, 0, sizeof(cnt));
sz = 1;
sum = 0;
}
void Insert(char *s){
int cur = 1;
int len = strlen(s);
sum += 2 * len;
for(int i=0; i<len; i++){
if(!chd[cur][ID[s[i]]])
chd[cur][ID[s[i]]] = ++sz;
cur = chd[cur][ID[s[i]]];
cnt[cur]++;
}
cur = 1;
for(int i=len-1; i>=0; i--){
if(!chd[cur][ID[s[i]]])
chd[cur][ID[s[i]]] = ++sz;
cur = chd[cur][ID[s[i]]];
cnt[cur]++;
}
}
int main(){
scanf("%d", &T);
for(int i='a'; i<='z'; i++) ID[i] = i - 'a';
while(T--){
scanf("%d", &n);
init();
for(int i=0; i<n; i++){
scanf("%s", str);
Insert(str);
}
ll ans = 0;
for(int i=2; i<=sz; i++){
ans += cnt[i] *cnt[i];
}
printf("%lld\n", sum * 2 * (ll)n - ans);
}
return 0;
}