zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    斐波那契数列 的两种实现方式(Java)
    单链表反转
    单链表合并
    两个有序list合并
    list去重 转载
    RemoveAll 要重写equals方法
    Java for LeetCode 138 Copy List with Random Pointer
    Java for LeetCode 137 Single Number II
    Java for LeetCode 136 Single Number
    Java for LeetCode 135 Candy
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/8073758.html
Copyright © 2011-2022 走看看