首先需要明白在什么情况下可以将某个长度排列完。比如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;
}