UVA-1282 Fibonacci单词

题目大意:
定义F(n)如下(下面的0、1表示01串“0”、“1”,加法表示连接):
求F(n)(n≤100)中非空01串p(|p|<105)出现的次数。

既然p是“串”,那么我们就用“串”的格式来保存它。定义count_p(s)为在字符串s中p出现的次数,则我们可以用递推。递推公式是: f ( n ) = f ( n − 1 ) + f ( n − 2 ) + f(n)=f(n-1)+f(n-2)+ f(n)=f(n1)+f(n2)+“跨区域的串”个数。我们用 O ( ∣ s ∣ ) O(|s|) O(s)算法求出count_p(s)可以让速度大大提升。如果用 O ( ∣ s ∣ 2 ) O(|s|^2) O(s2)算法,说不定就TLE了。
接下来的任务是求“跨区域的串”的个数。我们可以求出它们可能出现的位置(即起码有一个元素在左边,起码有一个元素在右边),然后用count_p运行这个区间,求出这个个数。
代码中用s1表示 f ( n − 2 ) f(n-2) f(n2),用s2表示 f ( n − 1 ) f(n-1) f(n1),用s3表示 f ( n ) f(n) f(n)
最后用指针交换,就可以用这些指针轮流使用3个z字符串而不用每次重置。

代码如下:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#define llong long long
using namespace std;
char p[100005],z1[400005],z2[400005],z3[400005];
int n,m,f[100005];llong ans[105];
int count_p(const char*s){
  int j=0,res=0;
  for(int i=0;s[i];i++){
    while(j&&s[i]!=p[j])j=f[j];
    if(s[i]==p[j])j++;
    if(!p[j])res++;
  }
  return res;
}
void ald(char*t,const char*s,int m){
  int l=strlen(s);
  if(l<=m)strcat(t,s);
  else strncat(t,s,m);
}
void ard(char*t,const char*s,int m){
  int l=strlen(s);
  if(l<=m)strcat(t,s);
  else strcat(t,s+l-m);
}
int main(){char*s1=z1,*s2=z2,*s3=z3;
  for(int o_o=1;cin>>n>>p;o_o++){m=strlen(p);
    strcpy(s1,"0");strcpy(s2,"1");
    f[0]=f[1]=0;
    for(int i=1;p[i];i++){
      int j=f[i];
      while(j&&p[i]!=p[j])j=f[j];
      f[i+1]=(p[i]==p[j]?j+1:0);
    }
    ans[0]=count_p(s1);ans[1]=count_p(s2);
    for(int i=2;i<=n;i++){s3[0]=0;
      ard(s3,s2,m-1);ald(s3,s1,m-1);
      ans[i]=ans[i-1]+ans[i-2]+count_p(s3);
      s3[0]=0;
      if(strlen(s2)>=m-1&&strlen(s1)>=m-1)
        {ald(s3,s2,m-1);ard(s3,s1,m-1);}
      else{strcat(s3,s2);strcat(s3,s1);}
      char*tmp=s1;s1=s2;s2=s3;s3=tmp;
    }
    cout<<"Case "<<o_o<<": "<<ans[n]<<endl;
  }
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值