传送门:UVA 11732
题意
给你n个单词,让他们两两比较,要求他们运用strcmp时,进行比较的次数。
根据题目数据量的范围,肯定不能用简单模拟,然后就想到比较时建Trie树,关键是考虑怎么计数,例如than和that它们比较的次数就是7次,因为他们是在a之后出现不同,就是要记录比较结束的位置,来进行计算即可,那么我们就来一个val数组代表计数器就好了,代表经过该节点的单词个数。
题解思路
第一发写的插入后遍历, T了, 然后改成插入时比较, 不过写搓了, T了好几发
主要是这个要记录比较次数巧一些, 貌似左孩子右兄弟trie树更好过, 不过不会敲。。。
这题主要是trie树的层次问题, 插入时更新比较次数, 省时不过处理有点麻烦
AC code:
/* adrui's submission Language : C++ Result : Accepted Love : yy Favorite : Dragon Balls Standing in the Hall of Fame */
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<bitset>
#include<map>
#include<cctype>
using namespace std;
#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define ls rt << 1, l, mid
#define rs rt << 1|1, mid + 1, r
#define lowbit(x) (x & (-x))
#define LL long long
#define REP(n) for(int i = 0; i < n; i++)
#define debug 0
const int maxn(4000005); //最大节点数
int N;
int ch[maxn][62];
int val[maxn];
int isend[maxn];
int idx(char p) { //每个节点分支节点的下标
if (isalpha(p)) {
if (islower(p))
return p - 'a' + 36;
return p - 'A' + 10;
}
return p - '0';
}
struct TrieTree {
int cnt;
LL ans;
TrieTree() {
cnt = 1;
ans = 0;
M(val, 0);
M(ch, 0);
M(isend, 0);
}
void insert_node(char *s) { //插入时比较
int len = strlen(s);
int u = 0;
for (int i = 0; i < len; ++i) {
int tmp = idx(s[i]);
if (!ch[u][tmp]) { //当前分支找不到改点, 累加上一层比较次数
ans += val[u] * (i << 1 | 1);
ch[u][tmp] = cnt++;
}
else {
ans += (val[u] - val[ch[u][tmp]]) * (i << 1 | 1);//找到, 也累加不过暂时要排除可继续比较的分支
}
++val[u]; //更新该点权值
u = ch[u][tmp];
}
ans += isend[u] * (2 * len + 2); //之前的相同单词比较
ans += (val[u] - isend[u]) * (2 * len + 1); //到达串尾部, 这个累加同上方原理
++val[u];
++isend[u]; //更新该单词出现个数
}
};
int main() {
#if debug
freopen("in.txt", "r", stdin);
#endif //debug
cin.tie(0);
cin.sync_with_stdio(false);
char s[1010];
int kase = 0;
while (cin >> N, N) {
TrieTree root;
cout << "Case " << ++kase << ": ";
for (int i = 0; i < N; ++i) {
cin >> s;
root.insert_node(s);
}
cout << root.ans << endl;
}
return 0;
}
总结
- 刚开始有RE, 看错数组大小, 懵逼, 毛病改不了啊
- 数据结构问题最好尝试全用数组模拟, 这样找bug会容易很多
- 多刷题!