UVA - 11732 "strcmp()" Anyone? (字典树的处理)

来源:互联网 发布:mac万能钥匙怎么连接 编辑:程序博客网 时间:2024/04/29 08:06

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28438

1.对于这个题我们可以用字典树来做,我们可以统计前缀相同的字母个数,对于前缀相同的单词每个字母要比较两次(包括‘\0’)。又因为要把任意两个单词相比较。所以相同字母的比较次数为2*val[i[*(val[i]-1).对于不相等的单词,由题意可得这个仅比较一次,即同所得单词相比较次数为(n-val[i])*val[i].对所有的点求和即可。因为这样计数每个都多统计了一次,所以对这个答案要除以2.

2.关于字典树,我们可以用链表来写。也可以用二维数组实现。也可以用左儿子右兄弟的方式来实现。

指针字典树:

struct trie
{
    trie *next[28];
    int val;
};
trie *root;
inline trie *newnode()//申请字符节点
{
    trie *t;
    t=(trie*)malloc(sizeof (trie) );
    memset(t,0,sizeof (trie) );
    return t;
}

void ins(trie *s,char x[])//字典树插入字符
{

    int i;
    trie *t;
    for(i=0;x[i]!='\0';i++)
    {

        if(s->next[x[i]-'a'])
        {
            s=s->next[x[i]-'a'];
        }
        else{
            t=newnode();
            s->next[x[i]-'a']=t;
            s=t;
        }
        s->val++;//统计项
    }
}

二维数组的字典树:

struct trie
{
    public:
    int ch[maxn][30];//ch储存字符节点
    int val[maxn];
    int sz=1;
   void  tt()  {   sz=1;   memset(ch[0],0,sizeof(ch[0]) );  };
    int idx(char c)  {  return c-'a';  }//字符表转换

    void insert_(char *s2,int v)//如果该字符不存在于字典树中,则初始化结点。
    {
        int u=0,n=strlen(s2);
        for(int i=0;i<n;i++)
        {
            int c=idx(s2[i]);
            if(!ch[u][c])
            {
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz]=0;
                ch[u][c]=sz++;
            }
            u=ch[u][c];//向下访问
        }
        val[u]=v;//统计项
    };
}


左儿子右兄弟的表示方法:

class trie
{
    public:
    int head[maxn],next[maxn],val[maxn],sz;//head[i]表示i结点的左孩子,next[i]表示i节点的右兄弟
    char ch[maxn];
    void init(){ sz=1,ch[0]=val[0]=head[0]=next[0]=0;}//值为0表示后面没有节点
    void ins(char *ss)//字符的插入
    {
        int v,u=0,len=strlen(ss);

        for(int i=0;i<=len;i++)
        {
            int f=0;
            for( v=head[u];v!=0;v=next[v])/*先在当前兄弟里面找到是否有与
s[i]相同的节点,没有则创建一个兄弟节点。有则继续向下遍历。*/
            {
                if(s[i]==ch[v])
                {
                    f=1;
                    break;
                }
            }
            if(!f)//没找到与s[i]相等的节点,把这个字符插入到最左边,即这个成为u节点的左儿子。则之前的左儿子就变成v的右兄弟。v没有左二子。
            {
                v=sz++;
                val[v]=0;
                ch[v]=s[i];
                next[v]=head[u];
                head[u]=v;
                head[v]=0;
            }
            u=v;//向下遍历
            val[v]++;
        }
   }
}t;

对于这三种表示方法,用链表动态申请内存很明显要复杂的多。而对于第二种每次申请一个节点都需要将其相应的子节点初始化完。这样初始化既耗费一定的时间。数组的粗存对于内存的占用可能也有消耗(这题用第二种写一直是RE,不知道是怎么回事。然后百度看到了第三种写法,1A)。感觉第三种写法的比上面的还是挺优化的了。在空间时间上都存在优化了。

AC:

#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<stdio.h>
using namespace std;
#define ll  long long int
#define maxn 8000008
const int mod=20071027;
char s[maxn];
class trie
{
    public:
    int head[maxn],next[maxn],val[maxn],sz;
    char ch[maxn];
    void init(){ sz=1,ch[0]=val[0]=head[0]=next[0]=0;}
    void ins(char *ss)
    {
        int v,u=0,len=strlen(ss);

        for(int i=0;i<=len;i++)
        {
            int f=0;
            for( v=head[u];v!=0;v=next[v])
            {
                if(s[i]==ch[v])
                {
                    f=1;
                    break;
                }
            }
            if(!f)
            {
                v=sz++;
                val[v]=0;
                ch[v]=s[i];
                next[v]=head[u];
                head[u]=v;
                head[v]=0;
            }
            u=v;
            val[v]++;
        }

    }

}t;
int main(void)
{
  int n;
  ll ans;
  int k=0;
  while(scanf("%d",&n)!=EOF)
  {
      t.init();
      if(n==0) break;
      ans=0;
      ll sum1=0;
      ll sum2=0;
      for(int i=0;i<n;i++)
      {
          scanf("%s",s);
          t.ins(s);
      }
      for(int i=1;i<t.sz;i++)
      {
         sum1+=t.val[i]*(t.val[i]-1);
         if(t.ch[i]=='\0')
         sum2+=(n-t.val[i])*t.val[i] ;
      }
      printf("Case %d: %lld\n",++k,sum1+sum2/2);
  }


}


0 0
原创粉丝点击