【自动机】
【自动机】DFA
概念与定义
自动机是一个对信号序列进行判定的数学模型
->结构:有向图


序列自动机
定义

举例



思路

代码示例
int pre[N],suf[N];
pre[0]=0;suf[n+1]=n+1;//初始化序列自动机
//之前:从前往后扫
for(int i=1;i<=n;i++){
pre[i]=pre[i-1];
if(s[i]=='1') pre[i]=i;//遇到1更新位置
}
//之后:从后往前扫
for(int i=n;i>=1;i--){
suf[i]=suf[i+1];
if(s[i]=='1') suf[i]=i;//遇到1更新位置
}
用法
对于每个字符(特别标记的字符)
可维护每个位置之前最近的位置和之后最近的位置
【题目整理】
1or0
https://ac.nowcoder.com/acm/contest/100253/E
思路
判断多少个子串包含1->判断该区间有多少个全0子串->减去即可
(1)建立
sum[N]维护有多少个全0子串—>前缀和思想/分块
序列自动机:pre[i]和suf[i]维护在该位置之前和之后的最近的1的位置
(2)查询

代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N=200010;
int n,q;
string s;
int l,r;
int pre[N],suf[N];//当前位置之前和之后最近的1
int sum[N];//分块维护全0子串个数(前缀和思想
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
cin>>s;
s=' '+s;
pre[0]=0;suf[n+1]=n+1;//初始化序列自动机
//序列自动机维护
//之前:从前往后扫
for(int i=1;i<=n;i++){
pre[i]=pre[i-1];
if(s[i]=='1') pre[i]=i;//遇到1更新位置
}
//之后:从后往前扫
for(int i=n;i>=1;i--){
suf[i]=suf[i+1];
if(s[i]=='1') suf[i]=i;//遇到1更新位置
}
for(int i=1;i<=n;i++){
if(s[i]=='1') sum[i]=sum[i-1];//遇到1就没有全0子串
else{
int nxt=suf[i],cnt=nxt-i;//下一个1的地方和当前全0串长度
//更新当前全0子串个数1:等差数列求和
//注意这里 块内所有都是一样的
for(int j=i;j<=nxt;j++) sum[j]=sum[i-1]+cnt*(cnt+1)/2;
i=nxt;//从下一个1开始查
}
}
cin>>q;
while(q--){
cin>>l>>r;
//特判:如果整个区间都是0:没有1->答案就是1
if(s[l]=='0' && s[r]=='0' && suf[l]>pre[r]){
cout<<'0'<<endl;
continue;
}
//该区间所有子区间个数
int ans=(r-l+1)*(r-l+2)/2;
//先去除左右两个区间的0串
if(s[l]=='0'){
int nxt=suf[l],cnt=nxt-l;
ans-=cnt*(cnt+1)/2;
l=nxt;//注意更新l和r
}
if(s[r]=='0'){
int pr=pre[r],cnt=r-pr;
ans-=cnt*(cnt+1)/2;
r=pr;//注意更新l和r
}
//减去中间0串的子串个数
ans-=sum[r]-sum[l-1];
cout<<ans<<endl;
}
return 0;
}

浙公网安备 33010602011771号