• UVA11732 strcmp() Anyone?(trie树)


    刘汝佳新书---------训练指南

    题意:求N个字符串两两比较,共比较了多少次?

    分析:将N个字符串插入前缀树,‘\0’也插入,这样就能区分字符串的终结点(以前我们都没插入)。这个很巧妙,不需要标识点标识字符终结点了,Orzz大牛!

    这样我们就能从前缀树中算出次数。

    字符串S1,S2的比较分这几种情况

    S1与S2的共同前缀S,则比较次数为len(S)*2+1

    但S1相同与S2,则比较次数为 len(S1+1)*2

    // File Name: 11732.cpp
    // Author: zlbing
    // Created Time: 2013/3/16 13:28:15
    
    #include<iostream>
    #include<string>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<set>
    #include<map>
    #include<vector>
    #include<cstring>
    #include<stack>
    #include<cmath>
    #include<queue>
    using namespace std;
    #define CL(x,v); memset(x,v,sizeof(x));
    #define INF 0x3f3f3f3f
    #define LL long long
    #define REP(i,n) for(int i=0;i<n;i++)
    #define REP1(i,n) for(int i=1;i<n+1;i++)
    const int maxnode = 4000 * 1000 + 10;
    const int sigma_size = 62;
    
    // 字母表为全体小写字母的Trie
    struct Trie {
      int ch[maxnode];
      int val[maxnode];
      int mark[maxnode];
      int head[maxnode];
      int flow[maxnode];
      int sz; // 结点总数
      void clear() { sz = 1; mark[0]=head[0]=flow[0]=0; } // 初始时只有一个根结点
      int idx(char c) {
         if(c>='0'&&c<='9')return c-'0';
        else if(c>='A'&&c<='Z')return c-'A'+10;
        else if(c>='a'&&c<='z')return c-'a'+36;
        else return 63;
      } // 字符c的编号
    
      // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
      void insert(const char *s) {
        int u = 0, n = strlen(s);
        int v;
        mark[0]++;
        for(int i = 0; i <=n; i++) {
          int c = idx(s[i]);
          bool found=false;
          for(v=head[u];v!=0;v=flow[v]){
            if(ch[v]==c){
                found=true;break;
            }
          }
          if(!found) { // 结点不存在
            val[sz] = 0;  // 中间结点的附加信息为0
            mark[sz]=0;
            v=sz++; // 新建结点
            ch[v]=c;
            flow[v]=head[u];
            head[u]=v;
            head[v]=0;
          }
          u =v; // 往下走
          mark[u]++;
        }
        val[u]++; // 字符串的最后一个字符的附加信息为v
      }
      /*一开始比较弱的比较方法。。。冏*/
      LL count(const char *s){
          int u=0,n=strlen(s);
          int c=idx(s[0]);
          int v;
            for(v=head[u];v!=0;v=flow[v]){
            if(ch[v]==c){
                break;
            }
          }
          int k=v;
          LL ans=0;
          LL t=0;
          for(v=head[u];v!=0;v=flow[v]){
            if(v!=k)t+=mark[v];
          }
          ans+=t;
          for(int i=0;i<n-1;i++){
              int c=idx(s[i]);
              for(v=head[u];v!=0;v=flow[v])
              if(ch[v]==c)break;
               u=v;
               c=idx(s[i+1]);
               for(v=head[u];v!=0;v=flow[v])
              if(ch[v]==c)break;
               int next=v;
              if(val[u]){
                ans+=(i+1)*2*val[u];
              }
                  if(mark[next]!=mark[u]){
                      ans+=(i*2+1)*(mark[u]-mark[next]);
                  }
          }
          c=idx(s[n-1]);
          for(v=head[u];v!=0;v=flow[v])
          if(ch[v]==c)break;
          u=v;
          t=0;
          for(v=head[u];v!=0;v=flow[v])
          t+=mark[v];
          if(t)
          ans+=t*(n*2)+1;
          ans+=(mark[u]-t-1)*(n);
          return ans;
      }
      LL dfs(int depth,int u){
        LL ans=0;
        if(head[u]==0){
            ans+=mark[u]*(mark[u]-1)*depth;
        }
        else{
            LL sum=0;
            for(int v=head[u];v!=0;v=flow[v]){
                sum+=mark[v]*(mark[u]-mark[v]);
            }
            ans+=sum/2*(2*depth+1);
            for(int v=head[u];v!=0;v=flow[v]){
                ans+=dfs(depth+1,v);
            }
        }
        return ans;
      }
    };
    const int stringnode=4011;
    const int stringlen=1000+10;
    char str[stringnode][stringlen];
    Trie solver;
    void out(LL a)
    {
        if(a < 0) {putchar('-'); a = -a;}
        if(a >= 10)out(a / 10);
        putchar(a % 10 + '0');
    }
    int in()
    {
        int flag = 1;
        char ch;
        int a = 0;
        while((ch = getchar()) == ' ' || ch == '\n');
        if(ch == '-') flag = -1;
        else
        a += ch - '0';
        while((ch = getchar()) != ' ' && ch != '\n')
        {
            a *= 10;
            a += ch - '0';
        }
        return flag * a;
    }
    int main(){
        int n;
        int cas=0;
        while(n=in())
        {
            if(!n)break;
            printf("Case %d: ",++cas);
            REP(i,n)scanf("%s",str[i]);
            solver.clear();
            LL ans=0;
            REP(i,n){
                solver.insert(str[i]);
    //            ans+=solver.count(str[i]);
            }
            ans=solver.dfs(0,0);
    //        printf("%lld\n",ans);
            out(ans);
            putchar('\n');
        }
        return 0;
    }
    // UVa11732 strcmp() Anyone?
    // Rujia Liu
    #include<cstring>
    #include<vector>
    using namespace std;
    
    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(包括最后的'\0'),沿途更新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;
        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;
      }
    };
    
    #include<cstdio>
    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\n", kase++, trie.count());
      }
      return 0;
    }
  • 相关阅读:
    HAProxy从零开始到掌握
    学会这15点,让你分分钟拿下Redis数据库
    求一个字符串长度
    js时间戳怎么转成日期格式
    js获取url参数值的两种方式
    js 处理URL实用技巧
    jQuery操作radio、checkbox、select 集合
    js处理url的技巧和归纳
    ajax hash缓存
    jquery ajax跨域
  • 原文地址:https://www.cnblogs.com/arbitrary/p/2964032.html
Copyright © 2020-2023  润新知