题意:给字符串,q个询问[l,r],每次询问至少删除多少个字符使得[l,r]只含子串“2017”不含“2016” ,不要求连续。
思路:
每一个线段树节点维护dp[i][j],表示经过线段前已经有"2017"子串的i位,经过线段后有“2017”的j位,要删除的最少字符个数。
然后区间dp转移。注意细节。
/* Author : Rshs
* Data : 2019-09-10-13.12
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const LL mod = 1e9+7;
const int MX = 2e5+5;
const int sz = 5;
int n,q;string s;
int ma[300];
struct no{int m[5][5];};
no T[MX<<2];
no ff(no x,no y){
no re;
for(int j=0;j<sz;j++) for(int k=0;k<sz;k++) re.m[j][k]=INT_MAX/10; //初始化为-1
for(int i=0;i<sz;i++){
for(int j=0;j<sz;j++){
for(int k=0;k<=j;k++){
re.m[i][j]=min(re.m[i][j],x.m[i][k]+y.m[k][j]); //左右转移
}
}
}
return re;
}
void pushUp(int rt){
T[rt]=ff(T[rt<<1],T[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l==r){
for(int j=0;j<sz;j++) for(int k=0;k<sz;k++) T[rt].m[j][k]=(k==j)?0:INT_MAX/10;//更新线段长度为1
char now=s[l-1];
if(now=='2') T[rt].m[0][0]=1,T[rt].m[0][1]=0;
if(now=='0') T[rt].m[1][1]=1,T[rt].m[1][2]=0;
if(now=='1') T[rt].m[2][2]=1,T[rt].m[2][3]=0;
if(now=='7') T[rt].m[3][3]=1,T[rt].m[3][4]=0;
if(now=='6') T[rt].m[4][4]=1,T[rt].m[3][3]=1;
return ;
}
int mid=(l+r)/2;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushUp(rt);
}
no query(int L,int R,int l,int r,int rt){
if(l>=L&&r<=R)
return T[rt];
int m=(l+r)>>1;
if(m<L)
return query(L,R,m+1,r,rt<<1|1);
if(m>=R)
return query(L,R,l,m,rt<<1);
return ff(query(L,R,l,m,rt<<1),query(L,R,m+1,r,rt<<1|1));
}
int main(){
cin>>n>>q;cin>>s;
build(1,n,1);
while(q--){
int l,r;scanf("%d%d",&l,&r);
int now=query(l,r,1,n,1).m[0][4];
if(now==INT_MAX/10)puts("-1");
else cout<<now<<'\n';
}
return 0;
}