确定性有限状态自动机 学习笔记

确定性有限状态自动机 学习笔记

例题

  • 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;
}
posted @ 2025-08-14 20:52  dengchengyu  阅读(13)  评论(0)    收藏  举报