思路:要求何寻找一个最短的 t,使得 t 不是 s(l,r) 的子序列,假设现在位于x,那么下一步有m个选择,我们要使得子序列尽可能的小,所以就要选择离x最远的那个字母,直到走出r为止。于是问题转化为:从 l 开始沿着 _next 一路往右跳,要跳多少步才能跳到 > r 的地方。求区间最值问题,使用st表,我们使用倍增的方法使得_next[i][j]: 是i从2^j步到达的最远位置。
#include<bits/stdc++.h> using namespace std; char s[200100]; int _next[200100][26];//st表 int v[2000100]; int a[26]; int main(){ int m,n; scanf("%d%d",&m,&n); scanf("%s",s+1); for(int i=0;i<=25;i++){ a[i]=n+1; } for(int i=n;i>=0;i--){ for(int j=0;j<m;j++){ v[i]=max(v[i],a[j]); } _next[i][0]=v[i]; if(i)a[s[i]-'a']=i; } for(int i=1;i<=n;i++){ cout<<v[i]<<' '; } for(int j = 1; j < 20; j ++){ for(int i = 0; i <= n; i ++) { int t = _next[i][j - 1]; if(t <= n) _next[i][j] = _next[t][j - 1]; else _next[i][j] = n + 1; } } int q; scanf("%d",&q); while(q--){ int l,r; scanf("%d%d",&l,&r); int ans = 0, now = l - 1; for(int j = 19; ~j; j --) if(_next[now][j] <= r) ans += (1 << j), now = _next[now][j]; printf("%d\n",ans+1); } return 0; }