【自动机】

【自动机】DFA

概念与定义

自动机是一个对信号序列进行判定的数学模型
->结构:有向图
image
image

序列自动机

定义

image

举例

image
image
image

思路

image

代码示例

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)查询

image

代码
#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;
}
posted @ 2025-01-20 15:19  White_ink  阅读(20)  评论(0)    收藏  举报