UVa 11732 trie好题

来源:互联网 发布:手机mp3变速软件 编辑:程序博客网 时间:2024/04/30 17:40

题目链接

题意:题目给出了标准strcmp()函数的代码,给你n个单词(n  <= 4000, len <= 1000, 大小写字母+数字),问你这些单词两两调用strcmp()函数一共比较了多少次。

分析:本题可以用trie做, 但按照一般的方法建字典树的话,会超时。最坏的情况 ,建立新节点的总体时间为 4000 *1000 * 62  == 2.5 * 10^8 很容易超时。

所以一般的建树方法肯定不行,我们必须优化建立新节点的时间。

建树思路:一个节点的62个儿子可以建立成链表,这样,建立一个新节点时就不用初始化其62个儿子节点了,大大优化了时间。

(我的做法比较繁琐,后来网上有位朋友给我分享了更为简单的做法,建树方法跟我一样,计数方法很容易理解,在我的做法后面)

我的做法:把所有的单词插入trie中,然后根据节点的信息计算所有情况。

我是一个字母一个字母统计其比较个数的

情况分析: 逐个字母比较可分为以下几种 

1. 两个不同字母的比较, 如 a, b, 算1次

2. 两个相同的字母的比较, 如 a,a, 算2次

3. 一个字母和一个终止符比较 , 如a, \0, 算1次

4. 两个终止符比较,如 \0, \0, 算2次

具体实现看 代码中的dfs

my dfs:

//val记录的是前缀为  根节点走到当前节点所走过字母  的子串个数和。//final记录 串为 根节点走到当前节点所走过字母   的个数和void dfs(int u) {      if(u != 1) ans += (LL) p[u].val * (p[u].val - 1);  //情况2,统计一次    ans += (LL) p[u].final * (p[u].final - 1);   //情况4, 统计一次    LL tmp = (LL) p[u].final * (p[u].val - p[u].final);  // 情况3,统计一次    int j = p[u].child;     while(j != -1) {         tmp += (LL) (p[u].val - p[j].val) * p[j].val;  // 这里 情况1 统计两次   和情况3 统计一次         dfs(j);         j = p[j].next;     }     ans += tmp/2;  // 情况1,3 都统计了两次,我们必须除以2.}

网友提供的做法:
我们可以一个串一个串的比较,这要方便很多。也避免了多情况的考虑。
直接上别人的代码,注解是我自己写的。
dfs部分代码:

void dfs(int depth, int u) {    if(head[u] == 0) // 两个串完全相同(终止符也考虑进去了) depth表示长度      ans += tot[u] * (tot[u] - 1) * depth;    else {      int sum = 0;      for(int v = head[u]; v != 0; v = next[v])        sum += tot[v] * (tot[u] - tot[v]); //统计最后一位字母不同的 串有几对(2个为一对)。      ans += sum / 2 * (2 * depth + 1); //相同字母有depth个,要算2次,所以 sum * 2*depth                                        // +1 加上最后一位是不同字母的个数。      for(int v = head[u]; v != 0; v = next[v])        dfs(depth+1, v);    }  }

my code:

#include <cstdio>#include <cstring>#include <cctype>#define LL long longstruct node {int val, final, child, next;int next_char, child_char;}p[4002 * 1002];int tot, n;LL ans;int idx(char c) { // translate the character into range 0-61.if(isupper(c)) return c-'A';if(islower(c)) return c-'a'+26;return c-'0'+52;}void insert(char *s) {int u = 1, i, j; // "u" means current nodep[u].val++;for(i = 0; s[i]; i++) {int k = idx(s[i]);if(p[u].child == -1) { // if current node has no children, create one.p[u].child = ++tot;p[u].child_char = k;p[tot].next = -1; p[tot].child = -1;p[tot].val = p[tot].final = 0;u = tot;}else if(p[u].child_char > k) { // if the first child is too big, we need to create a new first child.p[++tot].next = p[u].child; p[tot].next_char = p[u].child_char;p[u].child = tot; p[u].child_char = k;p[tot].child = -1;p[tot].val = p[tot].final = 0;u = tot;}else if(p[u].child_char == k)   // if the first child exactly matches, just descend.u = p[u].child;else { // walk the list of children to find the matching one, create one if needed.j = p[u].child;while(p[j].next != -1 && p[j].next_char < k) j = p[j].next;if(p[j].next != -1 && p[j].next_char == k) u = p[j].next;else {p[++tot].next = p[j].next;p[tot].next_char = p[j].next_char;p[j].next = tot; p[j].next_char = k;p[tot].child = -1;p[tot].val = p[tot].final = 0;u = tot;}}p[u].val++;}p[u].final++;}void dfs(int u) { // every pair of words that reaches here produces two comparisons.if(u != 1) ans += (LL) p[u].val * (p[u].val - 1); // pay attention the root, we needn't add it.ans += (LL) p[u].final * (p[u].final - 1);// every pair of words that differs exactly here produces one more comparison.LL tmp = (LL) p[u].final * (p[u].val - p[u].final);  int j = p[u].child;while(j != -1) {tmp += (LL) (p[u].val - p[j].val) * p[j].val;dfs(j);j = p[j].next;}ans += tmp/2; }char s[1005];int main() {int i, j, cas = 1;while( ~scanf("%d", &n) && n) {tot = 1;p[tot].child = p[tot].next = -1;p[tot].val = p[tot].final = 0;while(n--) {scanf("%s", s);insert(s);}ans = 0;dfs(1);printf("Case %d: %lld\n", cas++, ans);}return 0;}

	
				
		
原创粉丝点击