NEUOJ 720 头哥的烦恼

本文介绍了一种使用字典树解决单词最长公共前缀问题的方法,适用于ACM竞赛中的快速查找需求。通过构建字典树并运用LCA算法,实现了高效查询两个单词间的最长公共前缀。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目意思:头哥,众所周知,作为acm队的主力选手,最近遇到了一些烦恼,那就是CET-4临近了,但是他还有很多单词没记住.

现在头哥有n个单词没记住,(所有的单词长度加起来不超过5e5),他需要区分其中的m对单词,因为两个单词前面有一部分是一样的,所以他只需要记住后面不相等的部分,所以头哥想要知道每对单词最长相等的前缀长度。
题目连接:
http://202.118.31.226/problem/720
思路就是,建个字典树在,所为的最长公共前缀,可以这样求,即是,两个字符串的最近公共祖先LCA 的那个字母的深度。

需要注意一下的是。我在自己的电脑上 结构体里开了个26*100000的数组,就运行错误了,交到oj 能AC ;

#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
using namespace std;
const int maxnode=5*100000;
int f[maxnode];
int V=-1;
struct Trie
{
    int ch[maxnode][26];
    int val[maxnode];
    int sz;
    Trie() { sz=1; memset(ch[0],0,sizeof(ch[0]));}
    int idx(char c) {return c-'a';}

   void insert(char *s,int v,int num)
  {
    int u=0, n=strlen(s);
    for(int i=0;i<n;i++)
    {
        int c=idx(s[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;
      f[num]=u;
      V=max(V,u);
  }
};
char ss[100000*5+10];

int parent[32][100000*5+10];
int depth[100000*5+10];
void dfs(int v,int p,int d,Trie &t1)
{
    parent[0][v]=p;
    depth[v]=d;
    for(int i=0;i<26;i++)
    {
        if(t1.ch[v][i]!=0)
       {
         dfs(t1.ch[v][i],v,d+1,t1);
       }
    }
}
void init(int V)
{
    for(int k=0;k+1<32;k++)
    {
        for(int v=0;v<=V;v++)
        {
            if(parent[k][v]<0) { parent[k+1][v]=-1;
            }else {
                parent[k+1][v]=parent[k][parent[k][v]];
            }
        }
    }
}

  int lca(int u,int v)
  { // printf("look %d %d %d %d\n",u,v,depth[u],depth[v]);
    if(depth[u]>depth[v])
     swap(u,v);
     for(int k=0;k<32;k++)
     {
        if((depth[v]-depth[u])>>k&1){
            //  printf("depv%d depu%d %d  %d\n",v,u,depth[v]-depth[u],parent[k][v]);
            v=parent[k][v];

         }
     }
     if(u==v) return u;
     for(int k=32-1;k>=0;k--)
     {
        if(parent[k][u]!=parent[k][v])
        {
            u=parent[k][u];
            v=parent[k][v];
         }
     }
     return parent[0][u];
  }
 int main()
{
    //freopen("F:\\123.txt","r",stdin);
   int t,m,n;
    scanf("%d",&t);
    V=-1;
   for(int ik=1;ik<=t;ik++)
    {
     printf("Case #%d:\n",ik);
        //memset(parent,0,sizeof(parent));
        //memset(depth,0,sizeof(depth));
           Trie t1;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ss);
            t1.insert(ss,1,i);
        }
            dfs(0,-1,0,t1);
            init(V);
        int u,v,pot;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            pot=lca(f[u],f[v]);
            printf("%d\n",depth[pot]);
        }
    }
    return 0;
}


/*  int query(char *s)
 {
     int u=0,n=strlen(s);
     for(int i=0;i<n;i++)
     {
         int c=idx(s[i]);
         if(!ch[u][c])
         {
            return -1;
         }else
         {
            u=ch[u][c];
         }
     }
     return 1&&val[u];
  }  */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值