BSOI_2262.遥控车 (car.pas/c/cpp)

【问题描述】
平平带着韵韵来到了游乐园,看到了n辆漂亮的遥控车,每辆车上都有一个唯一的名字name[i]。韵韵早就迫不及待地想玩名字是s的遥控车。可是韵韵毕竟还小,她想象的名字可能是一辆车名字的前缀(也就是说能确定一个i,使s是name[i]的前缀),这时她就能玩第i辆车;或者是一个无中生有的名字,即s不是任何一辆车名字的前缀,这时候她什么也不能玩。
你需要完成下面的任务:
1.韵韵想了m个她想要的名字,请告诉她能玩多少次。
2.由于管理员粗心的操作,导致每辆车的摆放位置都可能出现微小的差错,原来第i辆车现在的位置可能是i-1、i、i+1中的任意一个(第1辆车的位置不可能是0,第n辆车的位置不可能是n+1)。请你计算出共有多少种可能的排列。
注:数据保证当s是name[i]的前缀时,i是唯一确定的。一辆车可以玩多次。
【文件输入】
第一行是2个正整数n、m。
接下来n行,每行1个字符串name[i],表示第i辆车的名字。接下来m行,每行1个字符串s,表示韵韵想要的名字。
【文件输出】
第一行输出韵韵能玩的次数。第二行输出共有多少种可能的排列。
【样例输入】
4 4
Abcd
DeF
AAa
aBccc
Ab
AA
AbC
aBcc
【样例输出】
3
5
【数据规模】
对于题目涉及到的字符串严格区分大小写,且长度小于255。
对于20%的数据 n≤10,m≤10;
对于40%的数据 n≤1000,m≤1000;
对于100%的数据 n≤10000,m≤10000。
【题目考点】
【思路点拨】
这道题设计两个基本独立的问题,一是字符串匹配问题,一个是找递推公式问题。
多次求字符串前缀匹配有以下几种优化方法:
1.对主串数组进行快排后,采用二分查找与匹配串比较。
2.采用字符树(trie树)进行主串记录,之后匹配
3.将主串数组与匹配串数组进行排序,之后进行线性扫描。[该方法只针对本题,因为题中说明了一个匹配串只能匹配一个主串]
方法2理论上很快,但实际效果可能比2、3慢,大多时候3很快,特快时匹配串很多时。一般采用方法1。
第二个问题是Fibonacci数列,f[i]=f[i-1]+f[i-2],f[1]=1;f[2]=2;多推几项就找到规律,这里要用高精度,采用滚动存储即可。

【方法一】:快排+二分查找+递推+高精度

#include<iostream>
using namespace std;
string s[10005],st,t;
int b[10005],a[10005],n;
void qsort(int L,int r)
{  int i=L,j=r;t=s[(L+r)/2];
   while(i<=j)
   {  while(s[i]<t)i++;
      while(s[j]>t)j--;
      if(i<=j)swap(s[i++],s[j--]);
   }
   if(i<r)qsort(i,r);
   if(L<j)qsort(L,j);
}
bool Find()
{  int L=1,r=n,mid;
   while(L<=r)
   {  mid=(L+r)/2;
      t=s[mid].substr(0,st.length());
      if(st==t)return 1;
      if(st<t)r=mid-1;else L=mid+1;
  }
  return 0;
}
void print(int a[])
{  if(a[0]==0)cout<<0;else for(int i=a[0];i>=1;i--)cout<<a[i];
   cout<<endl;
}
void jia(int a[],int b[])//高精度加法
{  int t[10005],i;
   t[0]=max(a[0],b[0]);
   for(i=1;i<=t[0];i++)t[i]=a[i]+b[i];
   for(i=1;i<=t[0];i++)t[i+1]+=t[i]/10,t[i]%=10;
   if(t[t[0]+1]>0)t[0]++;
   for(i=0;i<=b[0];i++)a[i]=b[i];
   for(i=0;i<=t[0];i++)b[i]=t[i];
}
int main()
{  int m,i,j,ans=0;
   cin>>n>>m;
   for(i=1;i<=n;i++)cin>>s[i];
   qsort(1,n);  //把车得名字从小到大快排
   for(i=1;i<=m;i++){cin>>st;if(Find())ans++;}//二分查找
   cout<<ans<<endl;
   a[0]=1;a[1]=1;b[0]=1;b[1]=2;
   for(i=3;i<=n;i++)jia(a,b);
   print(b);
   return 0;
}

【方法2】双指针+递推+高精度

【方法3】trie树+递推+高精度

#include<fstream>
using namespace std;
ifstream cin("car.in");
ofstream cout("car.out");
struct Tire
{  int ch[60];
   bool end; //结束标记
   void init(){end=0;memset(ch,0,sizeof(ch));}//trie树初始化
}tree[10005*70];
string s[10005],st;
int b[10005],a[10005],n,t[10005],cnt=0;
void Insert(string s) //trie树的插入
{  int i=0,x=0,y; //根为x=0
   while(i<s.length())
   {  y=s[i]-'A';
      if(tree[x].ch[y]==0) //原来没有节点,新增加节点
      {  tree[x].ch[y]=++cnt; //记录此节点的编号
         tree[cnt].init();//初始化
         x=cnt;
      }
      else x=tree[x].ch[y]; //有节点,沿着路径往下走
      i++;
   }
   tree[x].end=1;
}
int Find(string s) //查找s是否在trie树中
{  int y,i=0,x=0; //根为x=0
   while(i<s.length())
   {  y=s[i]-'A';
      if(tree[x].ch[y]==0)return 0;//表示没有在
      x=tree[x].ch[y];
      i++;
   }
   return 1; //返回查找成功
}

void print(int a[])
{  if(a[0]==0)cout<<0;
      else for(int i=a[0];i>=1;i--)cout<<a[i];
   cout<<endl;
}
void jia(int a[],int b[])//高精度加法 
{  int i;
   memset(t,sizeof(t),0);
   t[0]=max(a[0],b[0]);
   for(i=1;i<=t[0];i++)t[i]=a[i]+b[i];
   for(i=1;i<=t[0];i++){t[i+1]+=t[i]/10;t[i]%=10;}
   if(t[t[0]+1]>0)t[0]++;
   for(i=0;i<=b[0];i++)a[i]=b[i];
   for(i=0;i<=t[0];i++)b[i]=t[i];
}
int main()
{  int m,i,j,ans=0;
   cin>>n>>m;
   for(i=1;i<=n;i++){cin>>s[i];Insert(s[i]);}
   for(i=1;i<=m;i++){cin>>st;if(Find(st))ans++;}
   cout<<ans<<endl;
   a[0]=1;a[1]=1;b[0]=1;b[1]=2;
   for(i=3;i<=n;i++)jia(a,b);
   print(b);
   return 0;
}

我的做法: 二分查找+递推高精度

#include<cstdio>
#include<algorithm>
#include<cstring>
#define L 10001
#define N 5001
#define M 256
#define P 1000000
using namespace std;
struct name
{
    char s[M];
    bool operator<(const name &a)const
    {
        return strcmp(s,a.s)<0;
    }
}a[L];
void plus(int b[],int c[],int d[])
{
    for(int i=N;i>=1;i--)
    {
        d[i]+=b[i]+c[i];
        d[i-1]=d[i]/P;
        d[i]%=P;
    }
}
void print(int c[])
{
    int i=1;
    while(!c[i]) i++;
    printf("%d",c[i++]);
    for(int j=i;j<=N;j++)
        printf("%06d",c[j]);
}
int n,m,ans=0;
int main()
{
    freopen("car.in","r",stdin);
    freopen("car.out","w",stdout);  
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",a[i].s);
    sort(a+1,a+1+n);
    for(int i=1;i<=m;i++)
    {
        char s[M]="";
        scanf("%s",s);
        int l=1,r=n;        
        while(l<=r)
        {
            int mid=(l+r)/2;
            char t[M]="";
            strncpy(t,a[mid].s,strlen(s));
            if(strcmp(s,t)>0) l=mid+1;
            else if(strcmp(s,t)<0) r=mid-1;
                 else
                 {
                    ans++;
                    break;
                 }
        }
    }
    printf("%d\n",ans);
    int b[N+1],c[N+1],d[N+1];
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    b[N]=1,c[N]=2;  
    for(int i=3;i<=n;i++)
    {
        memset(d,0,sizeof(d));
        plus(b,c,d);
        memcpy(b,c,sizeof(c));
        memcpy(c,d,sizeof(d));
    }
    print(d);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值