zoukankan      html  css  js  c++  java
  • KMP自动机

    KMP自动机

    分类:字符串

    内容:详细版

    前置知识

    不会的可以点击链接(如果有)或者前往 OI-Wiki 学习

    • KMP

    一些约定

    • 字符集大小默认为m
    • 模板字符串默认为s
    • 文本字符串默认为t
    • |s|指字符串s的长度
    • 字符串下标默认从1开始

    简介

    KMP自动机主要用于字符串的匹配问题,预处理复杂度为O(|s|*m),可以以严格O(|t|)的复杂度进行字符串匹配(KMP为均摊O(|t|),并且可以处理可持久化字符串匹配问题

    同时KMP自动机也是AC自动机(可以处理多个模板串的匹配)的基础。

    构造KMP自动机

    KMP自动机与KMP的区别在于KMP自动机额外求出了 (trans_{i,j}) 表示在第i位置上往后匹配一个j字符会转移到什么状态(状态在这里指已经成功匹配了多少个字符)。

    在下文中,将用fail来代替KMPnextnxt代替上面的trans。

    普通地实现KMP自动机

    假设我们处理到了第i个状态并且前i-1个状态已经完全处理好了,当前的fail也指向了正确的位置。

    考虑每个nxt指向的状态:

    nxt[s[i + 1]]显然指向i+1

    其余的nxt应当指向一直跳fail后第一个下一个字符能匹配的位置,即:

    nxt[i][j] = i;
    while(nxt[i][j] && s[nxt[i][j] + 1] != j) nxt[i][j] = fail[nxt[i][j]];
    if(s[nxt[i][j] + 1] == j) nxt[i][j] = nxt[i][j] + 1;
    

    但是这样我们没有用到之前求出来的nxt并且复杂度很高,所以我们需要找到一种能用到之前求好了的nxt来快速计算当前nxt的方法。

    考虑nxt[fail[i]][j],这个表示的是fail[i]这个状态匹配一个j字符会转移到什么状态,即我们想在第i个状态后接一个j字符,但是s[i + 1]不是这个字符,我们就在fail[i]这个状态后面接着找并且找到了一个状态可以转移。

    仔细分析一下这个东西就是我们要求的nxt[i][j]

    证明一下:由于nxt[fail[i]][j]要么是从nxt[fail[fail[i]]][j]转移过来的,已经考虑过考虑跳多次fail,要么是直接在fail[i]后面接一个j字符,不需要跳多次fail,所以不用管跳多次fail,那么nxt[fail[i]][j]就是我们要求的nxt[i][j]了。

    // 假设字符串长度为 n,字符集大小为 m
    // nxt 一开始都是 0
    fail[1] = 0;
    nxt[0][s[1]] = 1;
    for(int i = 1; i < n; i ++) {
        for(int j = 0; j <= m; j ++) {
            if(s[i + 1] == j) nxt[i][j] = i + 1;
        	else nxt[i][j] = nxt[fail[i]][j];
        }
    }
    for(int i = 0; i <= m; i ++)
       	nxt[n][i] = nxt[fail[n]][i];
    

    这样写出来又长细节又多,一下没搞好就错了,最重要的是不好背,我们想办法缩成单独一个for循环。

    好写又好背的板子

    首先我们处理第i个状态时,我们可以先让所有的nxt[i]都是nxt[fail[i]],然后在第i+1个状态再把nxt[i][s[i+1]]设为i+1,这样我们就可以不用把第n个状态单独拿出来

    然后我们可以发现在求nxt[i]的时候不需要用到之前的fail所以我们可以不记录所有的fail

    for(int i = 1, fail = 0; i <= n; i ++) {
    	fail = nxt[fail][s[i]]; // 注意这一行不能和下一行互换
    	nxt[i - 1][s[i]] = i;
    	for(int j = 0; j < m; j ++)
    		nxt[i][j] = nxt[fail][j];
    }
    

    字符串匹配

    构造完以后匹配就很简单了,直接一直沿着nxt走就行了

    akakw1
  • 相关阅读:
    关于使用sudo找不到环境变量的问题
    HDFS修改副本数,并生效。
    Linux创建用户,SFTP只允许访问指定目录
    Docker 技术系列之安装Redis单机版和集群版
    Docker 技术系列之安装多版本Mysql5.6和Mysql5.7
    Docker 技术系列之安装Docker Desktop for Mac
    第五章 Mac系统软件-安装Java Web开发环境基本软件
    第四章 感受Mac之美-效率提高从操作快捷键开始
    第三章 感受Mac之美-万里挑一的装机必备软件
    第二章 感受Mac 之美-惊艳从Mac 外设开始,一周后的使用感受
  • 原文地址:https://www.cnblogs.com/akakw1/p/11604006.html
Copyright © 2011-2022 走看看