【学习笔记】DFA的构造

文章讨论了Myhill-Nerode定理在构造有限状态自动机(DFA)中的应用,解释了如何通过等价类来确定DFA的状态数,并提供了构造DFA的思路。此外,文章还提到了从NFA到DFA的转换以及状态数的理论复杂度问题。
摘要由CSDN通过智能技术生成

虽然平时做过但是考场上肯定还是不会,不过没事干还是写一下吧

Myhill-Nerode \text{Myhill-Nerode} Myhill-Nerode 定理:给定一个语言 L L L,定义在字符串上一个关系为,若对于所有的 z z z x z xz xz L L L中当且仅当 y z yz yz L L L中,则称 x , y x,y x,y在同一个等价类中。因此它把所有有限字符串的集合划分成一个或多个等价类。

Myhill-Nerode \text{Myhill-Nerode} Myhill-Nerode 定理声称在 L L L的最小自动机中状态的数目等价于在 L L L中诱导出的等价类的数目。

容易发现,语言 L L L可以被有限状态机接受,当且仅当等价类的数目是有限的。

Gym 102586J

考虑用等价类构造 D F A DFA DFA,还要为每一类找一个代表元。这里必须指出的是, L L L中的字符串一定在同一个等价类中,这个等价类也是接收点。

这里假定有限字符串集合长度不超过 L L L,然后暴搜求出每个字符串的等价类即可。

如何证明取 L = 10 L=10 L=10的正确性?思维小实验

假设存在一个 D F A DFA DFA d ( k ) d(k) d(k)能正确识别长度不超过 k k k的好串,据此可以构造出一个 N F A NFA NFA能正确识别长度不超过 k + 2 k+2 k+2的好串(其构造方法是,在原 D F A DFA DFA的基础上建立 ϵ \epsilon ϵ,然后建一个子 D F A DFA DFA表示操作的长度为 3 3 3的段,再用 ϵ \epsilon ϵ连回在原 D F A DFA DFA中所对应的字符边即可),再将其转化为 D F A DFA DFA d ( k + 2 ) d(k+2) d(k+2)(最常用的方法是幂极构造),并最小化。

如果 d ( k ) d(k) d(k)等价于 d ( k + 2 ) d(k+2) d(k+2),我们就能得到 d ( k ) = d ( k + 2 ) = d ( k + 4 ) = ⋯ d(k)=d(k+2)=d(k+4)=\cdots d(k)=d(k+2)=d(k+4)= ,这也就是我们所要求的 D F A DFA DFA。验证即可。

CF956F

考虑构造一个 F A FA FA来识别不超过 n n n位的 f ( m ) ≤ k f(m)\le k f(m)k的数字串

F A FA FA的状态是背包容量,字母表是 0 ∼ 9 0\sim 9 09,原来状态是 c c c,读入一个数字 d d d,可以转移到 c + d c+d c+d ∣ c − d ∣ |c-d| cd,显然这是一个 N F A NFA NFA,可以设置状态数为 100 100 100,然后大力幂集转移。

可以用长度为 100 100 100 bitset \text{bitset} bitset实现幂集,用一个哈希表记录某个 bitset \text{bitset} bitset出现过没有。

理论复杂度 O ( 2 100 ) O(2^{100}) O(2100)。这非常不科学。这种方法还是比较大胆的。

我完全没这个魄力好吧

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=1e5+5;
int n,K,tot,to[N][10],c[100],len;
ll l,r,dp[N][20][10];
map<__int128,int>id;
__int128 has(bitset<100>&b){
	__int128 x=0;
	for(int i=99;i>=0;i--){
		x*=2;if(b[i])x++;
	}return x;
}
int dfs(bitset<100>&b){
	int x;if(id[has(b)])return id[has(b)];
	x=id[has(b)]=++tot;
	for(int i=0;i<10;i++){
		if(b._Find_first()<=i)dp[tot][0][i]=1;
	}
	for(int i=0;i<10;i++){
		bitset<100>b2=(b<<i)|(b>>i);
		for(int j=0;j<i;j++)if(b[j])b2[i-j]=1;
		to[x][i]=dfs(b2);
	}return x;
}
ll dfs2(int x,int y,int z){
	if(!z)return dp[y][x][K];
	if(x==0)return dp[y][0][K];
	ll res=0;
	for(int i=0;i<=c[x];i++){
		res+=dfs2(x-1,to[y][i],i==c[x]);
	}return res;
}
ll solve(ll x){
	len=0;while(x)c[++len]=x%10,x/=10;
	return dfs2(len,1,1);
}
int main(){
	bitset<100>e;e[0]=1;
	int T;cin>>T,dfs(e);
	for(int l=0;l<10;l++){
		for(int i=1;i<=18;i++){
			for(int j=1;j<=tot;j++){
				for(int k=0;k<10;k++){
					dp[j][i][l]+=dp[to[j][k]][i-1][l];
				}
			}
		} 
	}
	while(T--){
		cin>>l>>r>>K;
		cout<<solve(r)-solve(l-1)<<"\n";
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值