题意:
给出一串数字序列,每次询问对于子串 ([l,r]) 至少删除多少个数字才能保证该子串无子序列 ('2016') 但有子序列 ('2017')。
(4 ≤ n ≤ 200 000, 1 ≤ q ≤ 200 000)
题目链接:https://codeforces.com/contest/750/problem/E
分析:
分别用数字表示以下状态:
(0:emptyset)
(1:'2')
(2:'20')
(3:'201')
(4:'2017')
构造一个 (5 imes 5) 的转移矩阵,(mat[i][j]) 表示 (i) 状态是否可以转移到 (j) 状态,如果可以则保存最小花费。而且,二者之间可以通过一个中间状态 (k) 来转换。通过矩阵的加法来去最小。
对于转换矩阵先初始为:除正对角线上为 (0),其余都为 (inf)。因为初始的时候状态 (i) 只能转换到状态 (i) 不需要删字符,转换到其他状态都是不可达的。
遍历到第 (x) 个字符 (s[x]) 时,对 (s[x]) 的情况进行讨论:
s[x]=‘2’:字符 (2) 只会影响状态 (0)→状态 (1) 的过程,因此由状态 (0) ->状态 (1) 不需要删字符,即 (mat[0][1]=0),但由状态 (0)->状态 (0),已经被破坏,因此为了维持状态 (0)->状态
(0) ,需要将字符 (2) 删除的结果存放在转换矩阵中,即 (mat[0][0]=1);而剩下的状态都不能加上字符 (2) 变成其余状态,故都为 (inf)。
s[x]=‘0’:字符 (0) 会影响状态 (1)->状态 (2) 过程,字符 (0,1,7) 道理都一样
s[x]=‘1’
s[x]=‘7’
s[x]=‘6’:对于字符 (6),由于不能包含 (2016),因此对于维持状态 (3(‘201’))->状态 (3(‘201’)),需删掉 (6),故 (mat[3][3]=1);对于状态 (4(‘2017’)),即若不删 (6),则会包含 (2016),因此要删去,即 (mat[4][4]=1),其余情况不做处理。
过程和 (AC) 自动机上跑 (DP) 类似,具体见代码。
代码:
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2e5+5;
struct matrix
{
int mat[5][5];
void init()
{
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
mat[i][j]=inf;
}
matrix operator +(const matrix &b)const
{
matrix res;
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
{
res.mat[i][j]=inf;
for(int k=0;k<5;k++)
res.mat[i][j]=min(res.mat[i][j],mat[i][k]+b.mat[k][j]);
}
}
return res;
}
}tree[N<<2];
char ss[N];
void build(int l,int r,int rt)
{
if(l==r)
{//构造转移矩阵:
tree[rt].init();
for(int i=0;i<5;i++) tree[rt].mat[i][i]=0;
if(ss[l]=='2') tree[rt].mat[0][0]=1,tree[rt].mat[0][1]=0;
if(ss[l]=='0') tree[rt].mat[1][1]=1,tree[rt].mat[1][2]=0;
if(ss[l]=='1') tree[rt].mat[2][2]=1,tree[rt].mat[2][3]=0;
if(ss[l]=='7') tree[rt].mat[3][3]=1,tree[rt].mat[3][4]=0;
if(ss[l]=='6') tree[rt].mat[4][4]=1,tree[rt].mat[3][3]=1;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
matrix query(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R)
return tree[rt];
int mid=(l+r)>>1;
matrix res;
res.init();
for(int i=0;i<5;i++) res.mat[i][i]=0;
if(L<=mid) res=res+query(l,mid,L,R,rt<<1);
if(R>mid) res=res+query(mid+1,r,L,R,rt<<1|1);
return res;
}
int main()
{
int n,q,l,r;
scanf("%d%d",&n,&q);
scanf("%s",ss+1);
build(1,n,1);
while(q--)
{
scanf("%d%d",&l,&r);
matrix ans=query(1,n,l,r,1);
printf("%d
",ans.mat[0][4]==inf?-1:ans.mat[0][4]);
}
return 0;
}