#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N =2e5+10;//2^18可倍增
int n,m,q,f[N][20],pos[26],nex[N][26];
//nex[i][j]:从i位置走最近的j字母边可到的位置
//f[i][j]:从i位置走1<<j步可达的最远位置(因为每步距离越远,所需步数越少)
string s;
int main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>m>>n>>s;s=" "+s;
for(int j=0;j<26;j++)nex[n][j]=n+1;
for(int i=n;i>=1;i--){
if(!pos[s[i]-'a']) nex[i][s[i]-'a']=n+1;
else nex[i][s[i]-'a']=pos[s[i]-'a'];
//先赋值给别人,再更新
pos[s[i]-'a']=i;
for(int j=0;j<m;j++){
if(!pos[j])nex[i-1][j]=n+1;
else nex[i-1][j]=pos[j];
}
}
for(int j=0;j<20;j++)//f[n+1][]不能漏,查询时会用到f[n+1][],f[0][]
f[n+1][j]=f[n][j]=n+1;
//从i位置往后走1(2^0)步,最远可达位置(到m个字母中任意一个)
for(int i=0;i<=n;i++){
for(int j=0;j<m;j++)
f[i][0]=max(nex[i][j],f[i][0]);
}
for(int j=1;j<20;j++)//倍增
for(int i=0;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
int l,r;
cin>>q;
while(q--){
cin>>l>>r;
//now走j字母边可以跳出区间右端r,即字母j未在now~r内出现,即存在子串,不是lr区间子串
int ans=0,now=l-1;//l-1可能取0
for(int j=19;j>=0;j--){
if(f[now][j]<=r){
ans+=(1<<j);now=f[now][j];//now可能取n+1
}
}
cout<<ans+1<<endl;//+1是跳出r的那一步
}
return 0;
}
倍增(找子序列A的minlen,使A不是序列B的子序列,B是字符串S的子串)
最新推荐文章于 2024-07-12 18:56:03 发布