POJ1451 - 字典树的变型

    上个周末给学校新队员上课...很是紧张..周日那场想来现场coding..就是很简单的高精度加法...结果怯场了..还是狐狸大大上来救了我..好丢人~~囧..这一向状态一直怪怪的~~这道题起码卡了三天吧~~其实这三天也是招新各种事~~没多大心思来做题~~昨天晚上终于把这题搞定了~~

    题意就是说给一个字典...并同时给出每个单词的出现次数...是想一下手机的输入法...然后按手机的输入一串按键~~每按一下按键输出一个推荐字串~~这个字串是在字典中次数最多的前缀..如果有两个相等的..输出字典序靠前的....如果输入一串按键,在字典里一个也没得.那就只能输出"MANUALLY"...其实感觉这题是很实用很现实的~~咱们平时用手机输入法就是有这个性质~~

    这道题还是的字典树...要求的是前缀出现次数最多..每个点的定义:

struct node
{
     int s[26],x,father,w; 
     char c;
}

    这道题因为有多组数据..动态分配空间如果不释放又很浪费..释放又很耗时...所以采取了开辟静态空间再来链接的伪指针方式....s[ ] 则是记录起孩子是否能链接到哪个位置...x是记录有多少个单词会经过这个点...所以在构造字典树时..每经过一个点..这个点的x就++...father是记录字典树中每个点的父亲..这里是为了这道题后面的输出方便..w是记录当前点所在层数..同样也是为了后面的处理方便...c是当前点是哪个字符..同样也是为了后面的处理方便....s[ ],father,x,w,c在构造树时都能得到准确的值....

    构造树的过程和我上一个日志类似..只是多加几个运算得到father,x,w,c而已了~~就直接看构造完树后怎么得到想要的结果..做的话用一个队列来维护..先是超级头结点0入内..可以想象一下...每一次读一个字符就往下做一层...而队列的目的就是看每一层还有多少点可以进行拓展.....每次读一个字符c..如果不是'1'就继续做...扫描所有还能符合条件的上一层的点..找到他们孩子中即在手机按键上是这个c-'0'又其存在...这种点先入队..然后判断其x值..找到所有孩子中满足条件的点..如果没有一个点能入队代表不存在符合输入信息的前缀了~~就要跳出输出"MANUALLY"...如果能找到最佳前缀...则输出..输出的话就从这个最佳的点开始用father往上走..直到走到father为0..就能得到这个前缀了..

    自己在写的时候调了很多bug...整了不少时间才搞定...还是自己队字典树不大熟练吧...囧...


Program:

#include<iostream>
#include<queue>
#define MAXN 1002
using namespace std;
struct node
{
     int s[26],x,father,w; 
     char c;
}a[10001];  
int t,T,w,m,i,p[MAXN],l,g,newdata;
int turn[26]={2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,7,8,8,8,9,9,9,9};
char s[MAXN][101],c; 
void find(int h,int k) 
{
     if (k>l) return; 
     a[h].x+=p[i];
     if (!a[h].s[s[i][k]-'a'])
     {
          g++;
          a[h].s[s[i][k]-'a']=g;     
          a[g].father=h;        
          a[g].w=k+1;   
          a[g].c=s[i][k];    
          if (k==l) a[g].x=p[i]; else a[g].x=0;             
     }   
     find(a[h].s[s[i][k]-'a'],k+1);  
}
queue<int> myqueue;
void make()
{   
     int h,k,m,i,step=0;
     char s[111];
     while (!myqueue.empty()) myqueue.pop();
     myqueue.push(0);
     while (scanf("%c",&c)!=EOF)
     {
         if (c=='1') return;  
         h=myqueue.front();
         m=0;
         while (a[h].w==step)
         {
              for (i=0;i<26;i++)
              if ((turn[i]==c-'0')&&(a[h].s[i]))
              {
                   myqueue.push(a[h].s[i]);
                   if (a[a[h].s[i]].x>m)
                   {
                       k=a[h].s[i];                                                 
                       m=a[k].x;
                   }                                                             
              }  
              myqueue.pop();
              if (myqueue.empty()) break;               
              h=myqueue.front();
         }                  
         if (!m) goto A;  
         step++;   
         s[step]='\0';       
         for (i=step-1;i>=0;i--)
         {
             s[i]=a[k].c;
             k=a[k].father;              
         } 
         puts(s);      
     }
     A:
     printf("MANUALLY\n"); 
     while (scanf("%c",&c)!=EOF)
     {              
         if (c=='1') return;
         printf("MANUALLY\n"); 
     }
     return;
}
int main()
{ 
     scanf("%d",&T);
     for (t=1;t<=T;t++)
     {
         scanf("%d",&w);
         g=0;
         memset(a,0,sizeof(a));  p[0]=0;
         for (i=1;i<=w;i++) 
         {
             scanf("%s%d",s[i],&p[i]);
             l=strlen(s[i])-1;
             find(0,0);
         }
         printf("Scenario #%d:\n",t);
         scanf("%d",&m);   a[0].x=0;
         while (m--)
         {              
             getchar();
             make();  
             printf("\n");  
         }
         printf("\n");
     }   
     return 0;   
}


    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值