ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

KMP字符串匹配

2022-07-07 13:05:08  阅读:144  来源: 互联网

标签:匹配 charAt int pattern next KMP 字符串 dp String


Knuth-Morris-Pratt

KMP算法是一种在文本串s中快速查找模式串p的一种算法。

动态规划实现

关键步骤:构建状态转移数组

package code;

/**
 * 动态规划实现KMP
 */
public class KMP {
    private int[][] dp;
    
    public int getIndex(String s, String pattern) {
        buildFSM(pattern);
        return search(s, pattern);
    }

    public void buildFSM(String pattern) {
        int m = pattern.length();

        /**
         * 状态转移数组
         * @param m 一共有 m 个状态
         * @param 26 本次只考虑小写字母字符串的匹配情况,可自由扩展
         */
        dp = new int[m][26];

        /**
         * 初始状态定义
         * @param prefixState 表示当前位置的前缀状态,匹配字符失败时要回退的状态
         */
        dp[0][pattern.charAt(0) - 'a'] = 1;
        int prefixState = 0;
        
        /**
         * 状态转移
         */
        for (int i = 1; i < m; i++) {
            for (int j = 0; j < 26; j++) {
                dp[i][j] = dp[prefixState][j];  // 全部使用前缀状态做初始填充
            }

            /**
             * 更新下一状态和前缀状态
             */
            dp[i][pattern.charAt(i) - 'a'] = i + 1;
            prefixState = dp[prefixState][pattern.charAt(i) - 'a'];
        }  
    }

    public int search(String s, String pattern) {
        int m = pattern.length();
        int n = s.length();
        int state = 0;

        for (int i = 0; i < n; i++) {
            state = dp[state][s.charAt(i) - 'a'];    // 计算下一状态
            
            // 到达最终状态, 匹配成功
            if (state == m) {
                return i - m + 1;
            }
        }

        return -1;
    }
}

传统实现

关键步骤:构建 next 数组

什么是 next 数组?

next 数组中保存着当前位置真前缀与真后缀的交集中最长元素的长度(真前缀的意思是不能是整个串,必须是子串)。

Example

模式串(p):"ababcabaa"

对应的 next 数组:

index 0 1 2 3 4 5 6 7 8
p a ab aba abab ababc ababca ababcab ababcaba ababcabaa
next 0 0 1 2 0 1 2 3 1
package code;

/**
 * 传统KMP实现
 */
public class KMP {
    private int[] next;

    /**
     * 计算最长公共前后缀长度, 构建 next 数组
     * @param pattern
     */
    public void buildNext(String pattern) {
        int m = pattern.length();
        next = new int[m];

        next[0] = 0;  // 单个字符只能为0

        /**
         * 将 pattern 右移1位, 自己匹配自己(用前缀匹配后缀)
         * @param i 指示后缀
         * @param j 指示前缀
         */
        for (int i = 1, j = 0; i < m; i++) {
            while (j != 0 && (pattern.charAt(i) != pattern.charAt(j))) {
                j = next[j - 1];
            }

            if (pattern.charAt(i) == pattern.charAt(j)) {
                j += 1;
            }

            next[i] = j;
        }
    }

    public int search(String s, String pattern) {
        for (int i = 0, j = 0; i < s.length(); i++) {
            /**
             * 字符匹配失败, 则回退到上一状态
             * i指针不变; j指针左移
             */
            while (j != 0 && s.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }
            
            if (s.charAt(i) == pattern.charAt(j)) {
                j += 1;
            }

            if (j == pattern.length()) {
                return i - j + 1;
            }
        }

        return -1;
    }
}

参考文章:

[1] KMP算法详解

[2] 算法学习笔记

标签:匹配,charAt,int,pattern,next,KMP,字符串,dp,String
来源: https://www.cnblogs.com/ylyzty/p/16454262.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有