词链

【题目描述】
给定一个仅包含小写字母的英文单词表,其中每个单词最多包含50个字母。
如果一张由一个词或多个词组成的表中,每个单词(除了最后一个)都是排在它后面的单词的前缀,则称此表为一个词链。例如下面的单词组成了一个词链:
i
int
integer
而下面的单词不组成词链:
integer
intern
请在给定的单词表中取出一些词,组成最长的词链。最长的词链就是包含单词数最多的词链。
数据保证给定的单词表中,单词互不相同,并且单词按字典顺序排列。
【输入格式】
第一行一个整数n(n<=100000),表示单词表中单词数
下接n行每行一个单词。
【输出格式】
一个整数,表示最长词链长度。
【样例输入】

5
i
int
integer
intern
internet

【输出格式】

4

【分析】
这题可以用多种方法做!
首先是暴力算法,复杂度 O(n2l) ,其中 l <script type="math/tex" id="MathJax-Element-7">l</script>为字符串的最大长度:

#include<bits/stdc++.h>
using namespace std;
const int N=3005;
int f[N];
char a[N][55];
int main(){
  int n;
  scanf("%d",&n);
  for (int i=1;i<=n;i++) scanf("%s",a[i]);
  f[1]=strlen(a[1]);
  for (int i=2;i<=n;i++)
    for (int j=1;j<i;j++){
      if (strlen(a[i])<=strlen(a[j])) continue;
      int flag=1;
      for (int k=0;k<strlen(a[j]);k++)
        if (a[i][k]!=a[j][k]) {flag=0;break;}
      if (flag) f[i]=max(f[i],f[j]+1);
    }
  int ans=0;
  for (int i=1;i<=n;i++) ans=max(ans,f[i]);
  cout<<ans;
}

方法1:用栈维护,最后输出栈的最大深度即可。因为题目已经按字典序排好,所以这种方法的正确性也就不难证明了。

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
char st[N][55],a[N][55];
int judge(char* s1,char* s2){
  int n=strlen(s1),m=strlen(s2);
  while (s1[n-1]==' ') n--;
  for (int i=0;i<n&&i<m;i++)
    if (s1[i]!=s2[i]) return 0;
  return 1;
}
int main(){
  int n,ans=1;
  scanf("%d",&n);
  for (int i=1;i<=n;i++) scanf("%s",a[i]);
  int top=1;
  for (int i=0;i<strlen(a[1]);i++) st[1][i]=a[1][i];
  for (int i=2;i<=n;i++){
    if (judge(st[top],a[i])){
      top++;
      for (int j=0;j<strlen(a[i]);j++) st[top][j]=a[i][j];
    }
    else{
      while(!judge(st[top],a[i])){
        for (int j=0;j<strlen(st[top]);j++) st[top][j]=' ';
        top--;
      }
      top++;
      for (int j=0;j<strlen(a[i]);j++) st[top][j]=a[i][j];
    }
    ans=max(ans,top);
  }
  cout<<ans;
}

方法2:极其明显的字典树啊,把每个字符看成是树的一个节点,然后读进来一个就加到树里,维护即可。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 51*100000
struct list{
  int id,num;
  struct list *next[27];
}*root;
int ans;
struct list *build(){
  int i;
  struct list *p;
  p=new list;
  p->id=0;
  p->num=0;
  for(i=0;i<27;i++) p->next[i]=0;
  return p;
}
void query(char str[]){
  int mn;
  mn=0;
  struct list *p;
  p=root;
  int n,j;
  n=strlen(str);
  for(j=0;j<n;j++){
    int c=str[j]-'a';
    if(p->next[c]==NULL) p->next[c]=build();
    p=p->next[c];
    mn=max(mn,p->num);
  }
  p->num=mn+1;
  ans=max(ans,mn+1);
}
int main(){
  int n,i;
  char str[101];
  scanf("%d",&n);
  ans=0;
  root=build();
  for(i=0;i<n;i++){
    scanf("%s",str);
    query(str);
  }
  printf("%d\n",ans);
}

方法3:Hash胡乱搞,把枚举前1~i-1个字符串的过程优化下,这里就不写代码了(并不会写)。
方法4:参考“拦截导弹”一题,用最长上升子序列的二分算法做,注意拦截导弹那题是比较两个数,这题是比较两个字符串,反正我码力太渣写不对觉得代码比较复杂容易写错,而且复杂度跟前面的几个算法相比也不是很理想,所以不推荐这个思路(然而这是我第二个思路,第一个是字典树)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值