news 2026/10/27 17:06:54

【DIY系列:自己动手写分布式搜索引擎】第18篇:有限状态机:不只是编译原理的玩具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【DIY系列:自己动手写分布式搜索引擎】第18篇:有限状态机:不只是编译原理的玩具

上一篇【第17篇】查询加速:跳表与倒排链求交的艺术
下一篇【第19篇】编辑距离有限状态机:拼写纠错的底层武器


摘要

很多人上编译原理课时觉得有限状态机(FSM)就是考试题,工作后八辈子用不上。我当年也这么想,直到写搜索引擎才发现:分词器、拼写纠错、正则匹配,背后全是 FSM 在干活。这一篇先用红绿灯、自动售货机这种生活比喻把 FSM 讲成人话,再手画一张状态转移表,最后点明它在搜索里的两大主战场:把文本切成词的分词器,以及容忍拼写错误的模糊匹配。下一篇的编辑距离纠错,就是踩在它肩膀上的。

一、FSM 是啥:用生活比喻说人话

有限状态机,名字吓人,本质特简单:一个东西有若干个"状态",收到不同"输入"就从一个状态跳到另一个状态,跳转规则是固定的。

红绿灯就是最直观的 FSM:

【红绿灯的状态转移】 状态: 红 ──(时间到)──▶ 绿 ──(时间到)──▶ 黄 ──(时间到)──▶ 红 输入: 只有一个"时间到"信号 规律: 红→绿→黄→红, 永不乱套

自动售货机更说明问题:你有"投币""按按钮"两种输入,状态在"待机→已投币→出货"之间跳。

【自动售货机 FSM】 待机 --投币--> 已投币 --按可乐--> 出货 --取货--> 待机 待机 --按可乐--> 待机(没投币, 不动) 已投币 --投币--> 已投币(余额增加)

看明白没?FSM 三件套:状态集合、输入集合、转移规则。给定当前状态和输入,下一状态是唯一确定的。这就是"有限"的含义:状态数有限、规则有限。

二、状态转移表:把 FSM 画成表

把上面的转移规则写成一张表,程序员和机器都好用。行是当前状态,列是输入,格子是下一状态。

【自动售货机 状态转移表】 投币 按可乐 取货 待机 已投币 待机 待机 已投币 已投币 出货 已投币 出货 出货 出货 待机

搜索引擎里的 FSM 通常还带"产出":进入某状态(或经过某条边)时吐出一个词、记一个标记。分词器就是这么干的:扫描字符流,遇到"词的边界"就从"收集字符"状态跳到"吐词"状态。

三、FSM 在搜索的第一战场:分词

回想我们前几篇用的SimpleAnalyzer,按空格和标点切词。那其实是一个极简 FSM:在"单词内"状态和"分隔符"状态间跳,遇到分隔符就把攒下的字符当成一个词吐出来。

【英文分词 FSM(简化)】 状态A(收集词) --字母/数字--> 状态A (继续攒) 状态A(收集词) --非字母数字--> 吐出一个词 → 状态B(跳过分隔) 状态B(跳过分隔) --非字母数字--> 状态B 状态B(跳过分隔) --字母/数字--> 状态A (开始攒新词)

中文分词更复杂(没有空格),但骨架一样:用状态机扫描字流,结合词典判断"这几个字凑成一个词"。工业分词器本质是一个带词典的 FSM,Lucene 的 Analyzer 流水线里每个 TokenFilter 也可以看成状态处理的一环。你写自己的 Analyzer 时,脑子里想的就是"我现在在哪个状态、看到这个字该跳哪"。

四、FSM 在搜索的第二战场:模糊匹配

FSM 第二个大用场是"模式匹配"。正则表达式引擎底层就是 NFA/DFA(非确定/确定有限自动机),本质都是 FSM。搜索引擎里,模糊匹配(比如"以什么前缀开头"“匹配某种模式”)同样能用 FSM 表达。

【匹配"以 '手机' 开头"的 FSM】 起始 --手--> S1 --机--> 命中(后续任意字符都留在命中) S1 之前收到非"手" → 失败

更妙的是,后面要讲的编辑距离纠错,可以把"允许错 k 个字符的所有字符串"直接建成一个 FSM,查询时顺着 FSM 走,走得到的就是可纠错的候选。这就把"模糊"这件事变成了"在自动机里走一遍",比暴力枚举快得多。

五、动手画一个:识别"数字词"的 FSM

光说不练假把式,写个小 FSM 识别连续数字并当成词吐出来。

importjava.util.*;/** 一个迷你 FSM:从字符流里抠出连续数字当作"数字词" */publicclassNumberTokenizer{enumState{START,IN_NUMBER}publicList<String>tokenize(Stringtext){List<String>tokens=newArrayList<>();Statestate=State.START;StringBuilderbuf=newStringBuilder();for(charc:text.toCharArray()){booleanisDigit=Character.isDigit(c);switch(state){caseSTART:if(isDigit){buf.append(c);state=State.IN_NUMBER;}break;caseIN_NUMBER:if(isDigit){buf.append(c);// 继续攒}else{tokens.add(buf.toString());// 边界到了, 吐词buf.setLength(0);state=State.START;}break;}}if(state==State.IN_NUMBER&&buf.length()>0){tokens.add(buf.toString());// 收尾}returntokens;}}// 测试: tokenize("价格299元 库存50") -> ["299", "50"]

这段代码的switch就是状态转移表的直接翻译:START收到数字跳IN_NUMBER,IN_NUMBER收到非数字就吐词跳回START。FSA 落地就是这么朴实。

六、为什么搜索引擎离不开 FSM

回到全文视角,FSM 在搜索引擎里扮演的是"确定性处理器":同样的输入永远走出同样的路径,可预测、可测试、可优化。分词要保证"同一个字符串永远切成同样的词",模糊匹配要保证"容错规则一致"。这种确定性,正是 FSM 的强项。

【FSM 在搜索引擎里的位置】 建索引: 文本 → [分词FSM] → 词项流 → 倒排 查 query: 文本 → [分词FSM] → 词项 → 查倒排 纠错: 错词 → [编辑距离FSM] → 候选正确词

下一篇,我们把 FSM 推到更刺激的用处:编辑距离纠错。你会看到,把"允许错几次"建成一个自动机,拼写纠错能从暴力枚举的灾难复杂度里被解救出来。

七、NFA 与 DFA:同一个意思的两种写法

聊深一点,FSM 分两种。NFA(非确定有限自动机)允许一个状态遇到同一个输入"有多个去处",甚至能"不消耗输入就跳转"(ε 转移),表达灵活。DFA(确定有限自动机)则规定每个状态对每个输入只有唯一去向,没有歧义,运行更快但可能状态数爆炸。

正则表达式引擎通常是先把正则转成 NFA,再(按需)转成 DFA 来执行。分词器、纠错自动机也在这个谱系里。我们手写版用的是"确定型"的状态机(switch 语句,一个输入一个去处),最简单也最好调试。理解 NFA/DFA 的取舍,你再看 Lucene 的 TokenStream 状态流转会觉得格外顺。

八、为什么正则匹配有时很慢

有个反直觉的点:写得不好的正则能让匹配慢到卡死。原因是某些正则(带嵌套量词如(a+)+)会构造出指数级路径的 NFA,回溯时组合爆炸。这提醒我们,FSM 虽强,表达式的写法直接决定它的复杂度。搜索引擎里的查询解析、分词规则,都得避免这种"回溯地狱"。把规则写成严格的状态机,而不是泛泛的正则,往往更稳更可预测。

九、FSM 给你的一层直觉

学完这一篇,我希望你带走一个直觉:很多"看似智能"的文本处理,底层都是"状态 + 转移"的确定性机器。分词是,纠错是,甚至你每天用的输入框联想也是。当你把问题建模成 FSM,它就从"玄学"变成了"画张状态表就能验证"的 engineering。下一篇的编辑距离纠错,正是用这台机器把模糊匹配变得可计算。

本篇小结

有限状态机不是考试玩具,它由"状态、输入、转移规则"三件套组成,红绿灯、售货机都是它的生活化身。在搜索引擎里 FSM 干两件大事:分词器靠状态跳转切词,模糊匹配靠自动机走通模式;后面编辑距离纠错同样建立在 FSM 上。动手写的数字分词器,本质就是状态转移表的直接翻译。下一篇,我们把 FSM 用在编辑距离纠错这个硬骨头上。

上一篇【第17篇】查询加速:跳表与倒排链求交的艺术
下一篇【第19篇】编辑距离有限状态机:拼写纠错的底层武器


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 17:06:44

YOLOv11-Pose人体姿态识别系统开发与优化实践

1. 项目概述&#xff1a;YOLOv11-Pose人体姿态识别系统 YOLOv11-Pose是基于YOLO系列最新架构开发的人体姿态识别解决方案&#xff0c;它在保持YOLO家族实时性优势的同时&#xff0c;通过改进的关键点检测头实现了更高精度的17点人体姿态估计。这个开源项目不仅包含完整的训练代…

作者头像 李华
网站建设 2026/10/27 17:06:00

openlava 4.0 源码编译、集群配置与作业调度实战

简介&#xff1a;openlava 4.0 源码安装包面向希望理解 HPC 集群调度与资源管理的开发者、运维人员和学习者&#xff0c;虽已停止官方维护&#xff0c;但其调度模型仍具参考价值&#xff0c;IBM LSF 亦由其发展而来。压缩包共 501 个文件、约 1.65MB&#xff0c;以 250 个 C 源…

作者头像 李华
网站建设 2026/10/27 17:05:05

Delphi 连 MySQL 5.7 驱动 DLL 配置排错

简介&#xff1a;面向仍在使用 Delphi 7 与 dbExpress 访问 MySQL 5.7 的开发者&#xff0c;这份压缩包提供经过项目实测的 libmysql.dll、dbxopenmysql50.dll 两个关键动态库及配套连接示例&#xff0c;重点解决网上版本杂乱、下载后难以正常连通数据库的问题。包内共 17 个文…

作者头像 李华
网站建设 2026/10/27 17:04:53

Linux特殊符号:系统命令的隐藏语言与实战技巧

1. Linux特殊符号&#xff1a;被忽视的系统语言 在终端里敲下第一个 ls -l 命令时&#xff0c;大多数人都不会注意到那些看似装饰性的符号。直到某天我调试一个Shell脚本&#xff0c;发现 2>&1 这样的组合能让错误输出神奇地重定向——那一刻我才意识到&#xff0c;…

作者头像 李华
网站建设 2026/10/27 17:04:49

Python量化交易策略与回测系统源码解析:从策略逻辑到实盘衔接

简介&#xff1a;这是一套面向计算机相关专业学生与量化交易初学者的Python量化交易策略及回测系统完整项目&#xff0c;源自经导师指导并通过评审的毕业设计&#xff0c;评审分98分&#xff0c;难度适中&#xff0c;适合用作毕业设计参考或项目实战练习。资源包共15个文件&…

作者头像 李华
网站建设 2026/10/27 17:03:45

Machine Configurator:工业设备可验证配置治理方案

简介&#xff1a;Machine Configurator 是一款专为西门子UG-NX平台开发的机床验证辅助工具&#xff0c;面向数控仿真工程师、CNC系统集成人员及高端制造领域技术人员&#xff0c;用于构建和调试机床运动学模型、CSE驱动程序与后处理器&#xff0c;解决实际加工前G代码行为验证难…

作者头像 李华