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

23 篇文章 0 订阅

寻找位置

题目描述

现在我们定义一个字符串序列 {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.                


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值