B. 攻防演练

Problem - B - Codeforces

首先需要明白在什么情况下可以将某个长度排列完。比如ababab,可以选取的集合为{a, b}
那么ab中可以选a或选b都行,又有三个ab,和排列组合的方式相似,可以证明一定可以选全长度为3的所以排列。
那么如何知道有多少个ab这样全部包含的序列呢。这时候就需要去记录后面各种字母的最近下标,从中选取最远的那一个就是可以包含集合里面全部字母的序列。
随后看这样的序列有多少种就可以得到需要多长的长度了。
其次还有如何快速处理得到长度的问题。在记录了各个字母最近下标的最远下标后,采用倍增的方式快速计算
所谓倍增的方式其实是每次都双倍跳跃,同时数也在双倍增加。当第一次跳到最远的地方的时候未必就是最长的长度
所以需要改变当前的起跳点后接着计算。

还有一个小点:本题需要输入输出的数据量大,不能使用c++的cin和cout。因为太慢了(在测试点4那里一定会超时),所以采用c语言的输入输出。

//首先需要明白在什么情况下可以将某个长度排列完。比如ababab,可以选取的集合为{a, b}
//那么ab中可以选a或选b都行,又有三个ab,和排列组合的方式相似,可以证明一定可以选全长度为3的所以排列。
//那么如何知道有多少个ab这样全部包含的序列呢。这时候就需要去记录后面各种字母的最近下标,从中选取最远的那一个就是可以包含集合里面全部字母的序列。
//随后看这样的序列有多少种就可以得到需要多长的长度了。
//其次还有如何快速处理得到长度的问题。在记录了各个字母最近下标的最远下标后,采用倍增的方式快速计算
//所谓倍增的方式其实是每次都双倍跳跃,同时数也在双倍增加。当第一次跳到最远的地方的时候未必就是最长的长度
//所以需要改变当前的起跳点后接着计算。
#include <iostream>
#include <cstdio>

using namespace std;

int nxt[35];
int dp[200006][35];

int main() {
	int m, n;
	scanf("%d %d", &m, &n);
	char s[200006];
	scanf("%s", s+1);
	for (int i=0;i<=30;i++) {
		nxt[i] = n+1;
		dp[n+1][i] = n+1;
	}
	for (int i=n;i>=0;i--) {
		for (int j=1;j<=m;j++) {
			dp[i][0] = max(dp[i][0], nxt[j]);
		}
		if (i==0) continue;
		nxt[s[i]-'a'+1] = i;
	}
	//进行一首倍增的扩展
	for (int i=n;i>=0;i--) {
		for (int j=1;j<=30;j++) {
			dp[i][j] = dp[dp[i][j-1]][j-1];
//			cout<<dp[i][j]<<" ";
		}
//		cout<<endl;
	}
	//开始计算最后的结果
	int ans;
	int q;
	scanf("%d", &q);
	while (q--) {
		int l, r, ans = 1;
		scanf("%d %d", &l, &r);
		int pos = l-1;
		for (int i=30;i>=0;i--) {
			if (dp[pos][i]&&dp[pos][i]<=r) {
				ans = ans+(1<<i);
				pos = dp[pos][i];
			}
		}
		printf("%d\n", ans);
	}
	return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值