C++——NOIP模拟题——寻找位置

    xiaoxiao2021-03-25  307

    寻找位置

    题目描述

    现在我们定义一个字符串序列 {S0,S1,S2,…} ,其中 S0=“A”,对于任意的 i>0,Si 可以由 Si-1 产生,具体产生方法为:替换 Si-1 中的每个字母“A”为“AAB”, 替换每一个字母“B”为“A”。

    比如按照此规则,前五个字符串为: S0 = “A” S1 = “AAB” S2 = “AABAABA” S3 = “AABAABAAABAABAAAB” S4 = “AABAABAAABAABAAABAABAABAAABAABAAABAABAABA” 请问:在字符串 S100 中,第 k 个“A”出现的位置。

    输入格式

    只有一行,一个正整数 k(1≤k<231)。

    输出格式

    只有一行,一个正整数,表示在字符串 S100 中,第 k 个“A”出现的位置。

    样例数据 1

    输入

    3

    输出

    4

    备注

    【数据范围】 对于 20% 的数据,满足1≤k≤100 对于 60% 的数据,满足1≤k≤500 对于 100% 的数据,满足1≤k<231

    解题报告:

    S0 = “A”

    S1 = “AAB” = S0S0B

    S2 = S1S1A = S1S1S0

    因此得到:Si = Si-1Si-1Si-2     (i>1)

    先求出Si-1和Si-2 分别包含A的个数,然后确定第k个A是出现在构成Si的三部分中的哪一部分,所以问题转化为在Si-1和 Si-2 中求第k个A的位置。

    a[i]表示Si 中字母A的个数,b[i]表示S中字母B的个数,则Si 的长度可以表示成:len[i]:=a[i]+b[i]。

    如果a[i-1]>=k,说明第k个A出现在Si的第一部分,只需要求出Si-1中第k个A出现的位置即可;

    否则,如果2*a[i-1]>=k,说明我们需要的第k个A是出现在Si的第二部分,需要求Si-1中第k-a[i-1]个A出现的位置即可,然后再加上len[i-1]的偏移量。

    如果不是以上两种情况,则说明第k个A是出现在Si的第三部分,需要求Si-2中第k-2*a[i-1]个A出现的位置即可,然后再加上2*len[i-1]的偏移量。

    实现方法:递归。

     

    a和b数组可以由Si的定义来递推计算

    由于S0 =“A”,有a[0]=1 ,b[0]=0

    因为 Si-1 中每个A都在Si 中产生2个A,而Si-1 中每个B都在Si 中变成了A,所以有:a[i]=2*a[i-1]+b[i-1];

    因为 Si-1 中每个A都变成AAB,所以Si 中B的个数等于Si-1 中A的个数 ,即: b[i]=a[i-1]。

    a[100]或b[100]会超过64位整数范围,注意到题目给出的k的范围是1≤k<231,通过计算发现a[25] > 231,因此只需要从S25 中确定第k个A出现的位置即可。

    时间复杂度:O(N)。

    #include<iostream> #include<cstdio> long long a[26],b[26],len[26]; inline long long ca(int n,int k) { if(n==0) return 1; if(a[n-1]>=k) return ca(n-1,k); else if(2*a[n-1]>=k) return len[n-1]+ca(n-1,k-a[n-1]); else return 2*len[n-1]+ca(n-2,k-2*a[n-1]); } int main() { int k; a[0]=1; len[0]=1; for(int i=1;i<=25;++i) { a[i]=2*a[i-1]+b[i-1]; b[i]=a[i-1]; len[i]=a[i]+b[i]; } scanf("%d",&k); printf("%lld",ca(25,k)); return 0; } var a,b:array[0..26]of longint; len,k:longint; i,j:integer; begin read(k); a[0]:=1; b[0]:=0; len:=0; j:=1; for i:=1 to 26 do if(j=1)then begin a[i]:=a[i-1]+a[i-1]+b[i-1]; b[i]:=a[i-1]; end; while(k>1)do begin for i:=0 to 26 do if(a[i]>=k)and(a[i-1]<k)then begin k:=k-a[i-1]; len:=len+a[i-1]+b[i-1]; break; end; end; len:=len+1; write(len); end.

    转载请注明原文地址: https://ju.6miu.com/read-87.html

    最新回复(0)