• 2017校赛 问题 D: 我知道了,你知道了吗?【递归】


    题目描述

    Alice和Bob走在去学校的路上,听到两个路人的对话:

    路人甲:我知道了, 你知道了吗?

    路人乙:我知道你知道了,你知道了吗?

    路人甲:我知道你知道我知道了,你知道了吗?

    路人乙:我知道你知道我知道你知道了,你知道了吗?

    .........

    他们便觉得十分有趣,觉得非常像两个人在玩字符串拼接的游戏,受此启发,Alice和Bob玩起了”类似”的拼接游戏:

    Alice首先会选择四个字符串S1, A, B, C,之后Alice和Bob轮流拼接出第2,第3,第4....第n个字符串,对于第i个字符串Si  (i > 1), 它的拼接规则是Si = A + Si-1 + B + Si-1 + C,其中符号“+”是字符串连接的意思,举个例子:

    Alice先选择了S1 = “ab”, A = “CD”, B = “EF”, C = “GH”,接下来的操作是:

    Bob 拼接出 S2 = “CDabEFabGH”

    Alice拼接出 S3 = “CDCDabEFabGHEFCDabEFabGHGH”

    ...........

    走在路上的你恰巧看到了Alice和Bob玩游戏的整个过程,Alice就问你,在他们拼出的第n个字符串中的第k个字符是什么?Alice和Bob都已经知道了,你知道了吗?假设无论多长的字符串Alice和Bob都能拼接出来。

    注意引号是定界符,不属于字符串。

    输入

    输入包括多组数据(数据组数总共不超过50)。

    每组第一行有四个字符串,分别是S1, A, B, C(长度都<=100),字符串只包含小写英文字母和大写英文字母.

    第二行一个整数q(1 <= q <= 50),表示Alice询问的次数。

    接下来有q行, 每组两个整数n(1 <= n <= 1000)和k(1 <= k <= 100000000),分别代表Alice和Bob拼接出的第n个字符串以及第n个字符串中的第k个字符。

    输出

    对于每个n和k,输出对应的Alice和Bob拼接出的第n个字符串中第k个字符。如果第n个字符串中第k个字符不存在输出’*’。每个答案独占一行。

    样例输入

    ab CD EF GH
    5
    1 1
    1 2
    1 3
    2 10
    2 11
    

    样例输出

    a
    b
    *
    H
    *

    越补题越崩溃,点背加经验少。。。。
    记录下个人思路:
    显然第n个字符串是个递推式:

    那么我求第n个字符串的第k个字符只需要判断k的范围即可,如果k<=len(A),就在A中找相应位置,如果k>len(A)&&k<=len(Sn-1)就递归到Sn-1,在Sn-1中找第k-len(A)个位置,以此类推。。。。

    所以这道题刚开始我这样写:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 int ls,la,lb,lc;
     8 int s[1006];
     9 char s1[105],A[105],B[105],C[105];
    10  
    11 void solve(int n,int k)
    12 {
    13     if(n==1){
    14         printf("%c
    ",s1[k-1]);
    15         return;
    16     }
    17     if(k<=la){
    18         printf("%c
    ",A[k-1]);
    19         return;
    20     }else if(k>la+s[n-1]&&k<=la+s[n-1]+lb){
    21         printf("%c
    ",B[k-la-s[n-1]-1]);
    22         return;
    23     }else if(k>la+lb+2*s[n-1]&&k<=la+lb+lc+2*s[n-1]){
    24         printf("%c
    ",C[k-la-lb-2*s[n-1]-1]);
    25         return;
    26     }else if(k>la&&k<=la+s[n-1]){
    27         solve(n-1,k-la);
    28         return;
    29     }else {
    30         solve(n-1,k-la-lb-s[n-1]);
    31         return;
    32     }
    33 }
    34  
    35 int main()
    36 {
    37     while(scanf("%s%s%s%s",s1,A,B,C)==4)
    38     {
    39         memset(s,0,sizeof(s));
    40         ls=strlen(s1);la=strlen(A);
    41         lb=strlen(B);lc=strlen(C);
    42         s[1]=ls;
    43         for(int i=2;i<=1001;i++){
    44             s[i]=s[i-1]*2+la+lb+lc;
    45         }
    46         int q,n,k;
    47         scanf("%d",&q);
    48         while(q--)
    49         {
    50             scanf("%d%d",&n,&k);
    51             if(k>s[n]) printf("*
    ");
    52             else{
    53                 solve(n,k);
    54             }
    55         }
    56     }
    57     return 0;
    58 }
    59  
    错误写法

     显然,运行错误(段错误),就是超数组边界了。后来再想想,len(Sn)=2*len(Sn-1)+len(A)+len(B)+len(C)==>len(Sn)=2^(n-1)len(s1)+(4*n-5)*[len(A)+len(B)+len(C)](大概推了一下,可能有错误)。可以看出,字符串的长度是呈指数形式增长的,自然存到n=30数就已经很大很大了,又有k<=10^8,那么也就是说即便S1,A,B,C长度均为1,n==28时已经大于这个数了(2^27约为134217728)。所以当n>=28时长度已经足够达到k的最大值。所以我只存到n=28,当n>28时根据递推式推到n<=28的范围内再用上面的做法解决。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 typedef long long ll;
     8 ll ls,la,lb,lc;
     9 ll s[31];
    10 char s1[105],A[105],B[105],C[105];
    11 
    12 void solve(int n,int k)
    13 {
    14     if(n==1){
    15         printf("%c
    ",s1[k-1]);
    16         return;
    17     }
    18     if(k<=la){
    19         printf("%c
    ",A[k-1]);
    20         return;
    21     }else if(k>la+s[n-1]&&k<=la+s[n-1]+lb){
    22         printf("%c
    ",B[k-la-s[n-1]-1]);
    23         return;
    24     }else if(k>la+lb+2*s[n-1]&&k<=la+lb+lc+2*s[n-1]){
    25         printf("%c
    ",C[k-la-lb-2*s[n-1]-1]);
    26         return;
    27     }else if(k>la&&k<=la+s[n-1]){
    28         solve(n-1,k-la);
    29         return;
    30     }else {
    31         solve(n-1,k-la-lb-s[n-1]);
    32         return;
    33     }
    34 }
    35 
    36 int main()
    37 {
    38     while(scanf("%s%s%s%s",s1,A,B,C)==4)
    39     {
    40         memset(s,0,sizeof(s));
    41         ls=strlen(s1);la=strlen(A);
    42         lb=strlen(B);lc=strlen(C);
    43         s[1]=ls;
    44         for(int i=2;i<=29;i++){
    45             s[i]=s[i-1]*2+la+lb+lc;
    46         }
    47         int q,n,k;
    48         scanf("%d",&q);
    49         while(q--)
    50         {
    51             scanf("%d%d",&n,&k);
    52             int pos=1;
    53             for(int i=1;i<=28;i++)
    54             if(s[i]>=k){
    55                 pos=i;break;
    56             }
    57             if(pos>n) printf("*
    ");
    58             else if(n<=28){
    59                 solve(n,k);
    60             }else {
    61                 int t=la*(n-28);
    62                 if(t>=k){
    63                     int r=k%la;
    64                     printf("%c
    ",A[r]);
    65                 }else{
    66                     solve(28,k-t);
    67                 }
    68             }
    69         }
    70     }
    71     return 0;
    72 }
  • 相关阅读:
    【实用代码】圆弧边框、阴影
    2015.7.30 第十五课 sql(新建数据库、创建表、注释、查询语句、新增、更新、删除、联合查询)
    【转】WEB前端开发工程师成长之路(计划)
    2015.7.29 第十三、十四课 jq实例(磁力图片、轮播图、腾讯课堂菜单、可弹出红色菜单、砸蛋游戏)
    【周末作业】2015.7.15 第二周 css作业及答案
    【课堂实例】轮播图
    2015.7.23 第十二课 课程重点(jq元素:获取、添加、删除、设置、绑定、显示隐藏、回调、动画)
    2015.7.22 第十一课 课程重点(jq:元素获取、添加、删除、过滤、判断、遍历、取值,样式设置、改变对象、切换、判断)
    【其它】喜欢的歌汇总
    2015.7.21 第十课 课程重点(jq选择器:id、类、标签、奇偶、eq、nth-child、子元素、属性、包含、位置、过滤)
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/8073758.html
Copyright © 2020-2023  润新知