确定性有限状态自动机 学习笔记
确定性有限状态自动机 学习笔记
例题
- abc418_g Binary Operation
- P12294 [THUPC 2025 决赛] 一个 01 串,n 次三目运算符,最后值为 1(加强版)
解决什么问题
有这样一类问题,给定一个含 01? 的串,? 可以变成 01 中的一个,现在你可以按一定规则操作这个串(比如把相邻字符按一定规则合并),问能不能把串变成 1 或问填 ? 的使其能变成 1 的方案数。
我们可以构建一个判断一个串是否能变成 1 的自动机,这样就能快速判断或在自动机上 DP。
我们把自动机上等价的状态缩在一起,如果操作的规则比较简单,自动机的点数会很少且是有限个。
构建自动机
定义等价:我们定义两个串 \(x,y\) 的状态是等价的,当且仅当对于任意的串 \(z\)(包括空串),\(xz\) 与 \(yz\) 的合法性相同,这里 \(xz\) 表示字符串的拼接。合法性指的是是否是题目所需的串,是一个布尔值,我们判断合法性相同就把两个串分别与所有 \(z\) 拼接得到布尔值序列,看两序列是否相同。
但是 \(z\) 有无数个,显然不能把所有 \(z\) 都与它们拼接判断等价,考虑设置一个合适的阈值 \(U\),我们取出所有长度小于或等于 \(U\) 的串与 \(x,y\) 拼接判断等价。而得到一个串的合法性可以使用朴素的暴力算法。
我们要得到所有本质不同的串,可以从空串开始做 BFS,每次往当前串后添加一个字符,然后检查是否属于已有的等价类。每个等价类第一个找到的字符串我们称之为这个等价类的代表元。
伪代码如下:
vector<string> Z; // 一个包含所有长度 U 以内的串集合(包括空串)
bool check(string x) {
// 一种暴力算法判断一个串是否是题目所需的串
}
vector<bool> get(string x) { // 获得与 Z 中串拼接而得的合法性序列,也可以把序列压成哈希值
vector<bool> res;
for(string i:Z) res.push_back(check(x+i));
return res;
}
vector<vector<int>> build() { // 返回一个图
int tot=0; // 点数
map<vector<bool>,int> Map; // 存储所有等价类的合法性序列,key 也可以用哈希值
vector<string> str; // 代表元
queue<string> q;
q.push(""); // 初始化空串
while(q.size()) {
string u=q.front(); q.pop();
vector<bool> res=get(u); // 获取合法性序列
if(Map.find(res)==Map.end()) { // 没有出现过,是新的等价类
Map[res]=tot++,str.push_back(u); // 新增点,并继续搜索
q.push(u+'0');
q.push(u+'1');
}
}
vector<vector<int>> graph(tot,vector<int>(2));
for(int i=0;i<tot;++i) { // 遍历所有点,加入转移边
graph[i][0]=Map[get(str[i]+'0')];
graph[i][1]=Map[get(str[i]+'1')];
}
return graph;
}

浙公网安备 33010602011771号