【学习笔记】DFA的构造

  • Post author:
  • Post category:其他



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




Myhill-Nerode

\text{Myhill-Nerode}







Myhill-Nerode






定理:给定一个语言



L

L






L





,定义在字符串上一个关系为,若对于所有的



z

z






z









x

z

xz






x


z









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






D


F


A





,还要为每一类找一个代表元。这里必须指出的是,



L

L






L





中的字符串一定在同一个等价类中,这个等价类也是接收点。

这里假定有限字符串集合长度不超过



L

L






L





,然后暴搜求出每个字符串的等价类即可。

如何证明取



L

=

10

L=10






L




=








10





的正确性?

思维小实验

假设存在一个



D

F

A

DFA






D


F


A








d

(

k

)

d(k)






d


(


k


)





能正确识别长度不超过



k

k






k





的好串,据此可以构造出一个



N

F

A

NFA






NF


A





能正确识别长度不超过



k

+

2

k+2






k




+








2





的好串(其构造方法是,在原



D

F

A

DFA






D


F


A





的基础上建立



ϵ

\epsilon






ϵ





,然后建一个子



D

F

A

DFA






D


F


A





表示操作的长度为



3

3






3





的段,再用



ϵ

\epsilon






ϵ





连回在原



D

F

A

DFA






D


F


A





中所对应的字符边即可),再将其转化为



D

F

A

DFA






D


F


A








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






D


F


A





。验证即可。



CF956F

考虑构造一个



F

A

FA






F


A





来识别不超过



n

n






n





位的



f

(

m

)

k

f(m)\le k






f


(


m


)













k





的数字串




F

A

FA






F


A





的状态是背包容量,字母表是



0

9

0\sim 9






0













9





,原来状态是



c

c






c





,读入一个数字



d

d






d





,可以转移到



c

+

d

c+d






c




+








d









c

d

|c-d|









c













d








,显然这是一个



N

F

A

NFA






NF


A





,可以设置状态数为



100

100






100





,然后大力幂集转移。

可以用长度为



100

100






100









bitset

\text{bitset}







bitset






实现幂集,用一个哈希表记录某个



bitset

\text{bitset}







bitset






出现过没有。

理论复杂度



O

(

2

100

)

O(2^{100})






O


(



2











100










)





。这非常不科学。这种方法还是比较大胆的。


我完全没这个魄力好吧

#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";
	}
} 



版权声明:本文为cqbzlydd原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。