确定有限状态自动机(Deterministic Finite Automation)

文章介绍了确定有限状态自动机(DFA)的概念,它用于判断输入字符串是否满足特定条件。以剑指Offer20题为例,展示了如何用DFA判断一个字符串是否表示数值,通过定义不同状态来处理整数、小数、指数等部分,并给出了具体的Java代码实现。

确定优先状态自动机(Deterministic Finite Automation, DFA)是一种计算模型。它包含一系列状态,这些状态中:

  • 有一个特殊的状态,被称作初始状态
  • 还有一系列状态被称为接受状态,它们组成了一个特殊的集合。其中,一个状态可能既是初始状态,也是接受状态

起初,这个自动机处于初始状态。随后,它顺序地读取字符串中的每一个字符,并根据当前状态和读入的字符,按照某个事先约定好的转移规则,从当前状态转移到下一个状态;当状态转移完成后,它就读取下一个字符。当字符串全部读取完毕后,如果自动机处于某个接受状态,则判定该字符串被接受;否则,判定该字符串被拒绝

在这里插入图片描述

如果输入的过程中某一步转移失败了,即不存在对应的转移规则,此时计算将提前中止。在这种情况下我们也判定该字符串被拒绝

确定有限状态自动机总是能够回答某种形式的**对于给定的输入字符串S,判断其是否满足条件P**的问题。

确定有限状态自动机驱动的编程,可以被看做一种暴力枚举方法的延伸:它穷尽了在任何一种情况下,对应任何的输入,需要做的事情。

自动机在计算机科学领域有着广泛的应用。在算法领域,它与大名鼎鼎的字符串查找算法KMP算法有着密切的关联;在工程领域,它是实现正则表达式的基础。

剑指 Offer 20. 表示数值的字符串为例:

class Solution {
    public boolean isNumber(String s) {
        Map<State, Map<CharType, State>> transfer = new HashMap<>();
        transfer.put(State.STATE_INITIAL, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_SPACE, State.STATE_INITIAL);
                    put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
                    put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
                    put(CharType.CHAR_SIGN, State.STATE_INT_SIGN);
                }}
        );
        transfer.put(State.STATE_INT_SIGN, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
                    put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
                }}
        );
        transfer.put(State.STATE_INTEGER, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
                    put(CharType.CHAR_EXP, State.STATE_EXP);
                    put(CharType.CHAR_POINT, State.STATE_POINT);
                    put(CharType.CHAR_SPACE, State.STATE_END);
                }}
        );
        transfer.put(State.STATE_POINT, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
                    put(CharType.CHAR_EXP, State.STATE_EXP);
                    put(CharType.CHAR_SPACE, State.STATE_END);
                }}
        );
        transfer.put(State.STATE_POINT_WITHOUT_INT, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
                }}
        );
        transfer.put(State.STATE_FRACTION, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
                    put(CharType.CHAR_EXP, State.STATE_EXP);
                    put(CharType.CHAR_SPACE, State.STATE_END);
                }}
        );
        transfer.put(State.STATE_EXP, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
                    put(CharType.CHAR_SIGN, State.STATE_EXP_SIGN);
                }}
        );
        transfer.put(State.STATE_EXP_SIGN, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
                }}
        );
        transfer.put(State.STATE_EXP_NUMBER, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
                    put(CharType.CHAR_SPACE, State.STATE_END);
                }}
        );
        transfer.put(State.STATE_END, new HashMap<CharType, State>() {{
                    put(CharType.CHAR_SPACE, State.STATE_END);
                }}
        );

        int len = s.length();
        State state = State.STATE_INITIAL;

        for (int i = 0; i < len; i++) {
            CharType type = toCharType(s.charAt(i));
            if (!transfer.get(state).containsKey(type)) return false;
            else state = transfer.get(state).get(type);
        }

        return state == State.STATE_INTEGER || state == State.STATE_POINT || state == State.STATE_END || state == State.STATE_FRACTION || state == State.STATE_EXP_NUMBER;
    }

    public CharType toCharType(char c) {
        if (c >= '0' && c <= '9') return CharType.CHAR_NUMBER;
        if (c == 'e' || c == 'E') return CharType.CHAR_EXP;
        if (c == '.') return CharType.CHAR_POINT;
        if (c == '+' || c == '-') return CharType.CHAR_SIGN;
        if (c == ' ') return CharType.CHAR_SPACE;
        return CharType.CHAR_ILLEGAL;
    }

    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,
        STATE_INTEGER,
        STATE_POINT,
        STATE_POINT_WITHOUT_INT,
        STATE_FRACTION,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUMBER,
        STATE_END
    }

    enum CharType {
        CHAR_NUMBER,
        CHAR_EXP,
        CHAR_POINT,
        CHAR_SIGN,
        CHAR_SPACE,
        CHAR_ILLEGAL
    }
}

状态一定要定义好,小数后的整数和小数的前的整数是不一致的,需要使用不同的状态STATE_INTEGERSTATE_FRACTION

参考文献:

  1. 表示数值的字符串
  2. 浅谈DFA确定有限状态自动机
### 有限自动机的概念及分类 #### 什么是有限自动机? 有限自动机(Finite State Automaton, FSA),也被称为有穷自动机,是一种用于建模计算系统的抽象机器。它的主要功能是对输入字符串进行处理,并决定该字符串是否属于某个特定的形式语言[^2]。 #### 有限自动机的分类 有限自动机可以根据其行为特征分为两种基本类型:确定性有限自动机(Deterministic Finite Automaton, DFA)和非确定性有限自动机(Non-deterministic Finite Automaton, NFA)。这两种类型的自动机在结构上存在显著差异,具体如下: --- #### 确定性有限自动机(DFA) DFA 是一种特殊的有限自动机,在任何给定状态下,对于每个可能的输入符号都只有一个明确的状态转移。这意味着: - 对于任意状态 \( q \in Q \) 和 输入字符 \( a \in \Sigma \),转移函数 \( \delta(q, a) \) 的结果总是唯一的一个状态。 - 不允许空移动(ε-move)的存在。 因此,DFA 的识别过程是完全确定性的,不需要回溯操作即可完成整个字符串的匹配[^3]。 --- #### 非确定性有限自动机(NFA) 相比之下,NFA 则具有更大的灵活性。在一个 NFA 中: - 同一状态下可能存在多个针对相同输入符号的有效转移; - 支持 ε 移动,即可以在不消耗任何实际输入的情况下改变当前所处的状态; - 当面临多条可行路径时,如果至少有一条能够成功接受,则认为整体运行有效。 尽管如此,理论上已经证明每台 NFA 均可被转化为一台等价的功能相同的 DFA 来实现同样的模式匹配任务[^4]。 --- #### 主要区别总结 | 特性 | **DFA** | **NFA** | |---------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------| | 转移规则 | 单一固定 | 可能有多重选择 | | 是否支持 ε 迁移 | 否 | 是 | | 接受条件 | 如果最终停驻的位置位于接收态之中则视为接纳 | 存在一序列动作使得最后达到终态就算作认可 | | 复杂度 | 较高 | 更低 | 上述对比表明虽然两者表面上有很大不同之处,但从表达能力上看它们并无本质差别——任一由 NFA 所描述的语言总能找到相应形式下的 DFA 描述之;反之亦然。 --- #### 如何将 NFA 转换为 DFA? 为了把一个给定的 NFA 转化成与其功能相等效的 DFA ,常用的方法叫做“子集构造算法”。此方法的核心思想在于利用幂集来扩展原初节点集合,从而形成新的单一实体作为目标设备中的单个结点代表原来那些潜在可能性组合而成的整体情况[^1]。 以下是 Python 实现这一转化逻辑的小例子: ```python def nfa_to_dfa(nfa_states, nfa_transitions, start_state, accept_states): dfa_states = [] unprocessed_states = [{start_state}] while unprocessed_states: current_set = unprocessed_states.pop(0) if current_set not in dfa_states: dfa_states.append(current_set) transitions = {} for symbol in nfa_transitions[next(iter(current_set))]: reachable = set() for state in current_set: try: reachable.update(nfa_transitions[state][symbol]) except KeyError: pass if reachable and reachable not in dfa_states: unprocessed_states.append(reachable) transitions[symbol] = reachable # Store the transition information somewhere... return {"states":dfa_states} ``` 注意这只是一个简化版框架示意代码片段,完整版本还需要考虑更多细节比如如何标记终止状态等问题。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值