【问题描述】
平平带着韵韵来到了游乐园,看到了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;
}