• "strcmp()" Anyone? UVA


    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    const int maxnode = 4000 * 1000 + 10;
    const int sigma_size = 26;
    
    // 字母表为全体小写字母的Trie
    struct Trie {
      int head[maxnode]; // head[i]为第i个结点的左儿子编号
      int next[maxnode]; // next[i]为第i个结点的右兄弟编号
      char ch[maxnode];  // ch[i]为第i个结点上的字符
      int tot[maxnode];  // tot[i]为第i个结点为根的子树包含的叶结点总数
      int sz; // 结点总数
      long long ans; // 答案
      void clear() { sz = 1; tot[0] = head[0] = next[0] = 0; } // 初始时只有一个根结点
    
      // 插入字符串s(包括最后的''),沿途更新tot
      void insert(const char *s) {
        int u = 0, v, n = strlen(s);
        tot[0]++;
        for(int i = 0; i <= n; i++) {
          // 找字符a[i]
          bool found = false;
          for(v = head[u]; v != 0; v = next[v])
            if(ch[v] == s[i]) { // 找到了
              found = true;
              break;
            }
          if(!found) {
            v = sz++; // 新建结点
            tot[v] = 0;
            ch[v] = s[i];
            next[v] = head[u];
            head[u] = v; // 插入到链表的首部,免除对兄弟链的遍历,这里兄弟先后无关系
            head[v] = 0;
          }
          u = v;
          tot[u]++;
        }
      }
    
      // 统计LCP为u的所有单词两两的比较次数之和
      void dfs(int depth, int u) {
        if(head[u] == 0) // 叶结点
          ans += tot[u] * (tot[u] - 1) * depth;//叶子节点的tot反映了有多少个自己
        else {
          int sum = 0;
          for(int v = head[u]; v != 0; v = next[v])
            sum += tot[v] * (tot[u] - tot[v]); // 子树v中选一个串,其他子树中再选一个
          ans += sum / 2 * (2 * depth + 1); // 除以2是每种选法统计了两次
          for(int v = head[u]; v != 0; v = next[v])
            dfs(depth+1, v);
        }
      }
    
      // 统计
      long long count() {
        ans = 0;
        dfs(0, 0);
        return ans;
      }
    };
    
    const int maxl = 1000 + 10;   // 每个单词最大长度
    
    int n;
    char word[maxl];
    Trie trie;
    
    int main() {
      int kase = 1;
      while(scanf("%d", &n) == 1 && n) {
        trie.clear();
        for(int i = 0; i < n; i++) {
          scanf("%s", word);
          trie.insert(word);
        }
        printf("Case %d: %lld
    ", kase++, trie.count());
      }
      return 0;
    }

    注释了一波lrj的代码。。

    与前文的Trie树不同,左孩子右兄弟树的每一个节点的大小没有sigma_size那么大

    这就大大节省了空间:通过时间上兄弟间的遍历链条关系来节约空间,整个结构没有无用节点

    暂且能掌握这种树的用法咱就算达到目的了吧orz

  • 相关阅读:
    ES6:Iterator遍历器
    前端:对BFC的理解
    前端:性能优化之防抖与节流
    ES6新增数据类型Symbol
    ajax和fetch、aixos的区别
    我对js数据类型的理解和深浅(copy)的应用场景
    egg的基本使用
    前端:css3的过渡与动画的基础知识
    Java基础篇之类
    JAVA基础篇之Scanner
  • 原文地址:https://www.cnblogs.com/Drenight/p/8611324.html
Copyright © 2020-2023  润新知