kmp算法

kmp 字符串匹配算法

  1. s1为原字符串,长n,s2为目的字符串长度m,n>=m,想在s1中找到连续的n2
  2. n*m级别的基本算法:从s1第i位开始,检查接下来的m位是否与s2相同,相同匹配成功,否则接着从i+1开始继续匹配m个字符。
  3. kmp算法目的是,第i次匹配可以使用i-1次匹配失败的结果
    1. 假如当前s1匹配到c1处,s2匹配到c2处,尝试复用c2之前的匹配结果,s2的前c2个字符串是与s1往前c2长的字符串相同的;
    2. 假设s2的前c2位中,前j个字符与后j个字符相同(最长前缀与最长后缀匹配——最多前j个字符与后j个字符相同),那么就可以从s1的c1处接着匹配s2的j处;c2从0到m-1,需要保存对应的j,这就是next数组,目标字符串才有next数组
  4. next数组求法,s位目标串
    1. next[0]为-1,标志着到了第0位(下面需要可以判断是否到了next的边缘),next[1]为0,表示当1处的字符不匹配,应该从0处开始重新匹配
    2. 从next[2]开始,使用next[0-i]的值计算next[i]:
      1. next[i]表示,s中第0到next[i]-1这next[i]位字符与i往前数next[i]位的字符匹配,
      2. 若i+1的值等于next[i]+1,则next[i+1]等于next[i]+1(第i+1位的最长前后缀为第i位加一)
      3. 否则查看next[next[i]]+1处字符是否与i+1处相同,这样next[i+1]为next[next[i]]+1
      4. 循环3,直到一直找到0处,这时next[i+1]为0

代码

public class KMP {
    public static void main(String[] a) {
        StringBuilder s1, s2;
        for(int i=0; i<1000; i++) {
            s1 = randomStr(3);
            s2 = randomStr(10);
            if((s2.indexOf(s1.toString()) != -1) && !match(s1.toString(), s2.toString())) {
                System.out.println("error");
                System.out.println("s1:" + s1);
                System.out.println("s2:" + s2);
                System.out.println();
                return;
            }
        }
        System.out.println("success");
    }

    public static StringBuilder randomStr(int n) {
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0; i<n; i++) {
            char c = (char)('a' + (int) (Math.random()*26));
            stringBuilder.append(c);
        }
        return stringBuilder;
    }

    public static int[] getNextArr(String s) {
        if(s == null || s.length() == 0) return new int[0];
        if(s.length() == 1) return new int[]{-1};
        if(s.length() == 2) return new int[]{-1, 0};
        int[] next = new int[s.length()];
        next[0] = -1;
        next[1] = 0;
        for(int i=2, j=0; i<s.length(); i++) {
            j = i-1;
            while(next[j]>-1 && s.charAt(i-1) != s.charAt(next[j])) {
                j = next[j];
            }
            next[i] = next[j] + 1;
        }
        return next;
    }

    public static boolean match(String s1, String s2) {
        int[] next = getNextArr(s1);
        for(int i=0, j=0; j<s2.length(); ) {

            if(s1.charAt(i) == s2.charAt(j)) {
                i++;
                j++;
                if(i == s1.length()) return true;
            } else {
                i = next[i];
                if(i == -1) {
                    i = 0;
                    j++;
                }
            }
        }
        return false;
    }
}

kmp jdk 实现

string的split方法

n^2级别的算法

### KMP算法的实现与原理 KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,其核心思想是通过构建部分匹配表(也称为`next`数组或`failure`函数),避免在模式串匹配失败时对文本串进行回溯,从而降低时间复杂度[^1]。 #### 一、KMP算法的核心原理 KMP算法的关键在于利用模式串的部分匹配信息,构建一个最长公共前后缀表(通常称为`next`数组)。当匹配失败时,算法根据`next`数组中的值跳转到适当的位置继续匹配,而不是简单地将文本串指针回退。这种方法确保了文本串只需被扫描一次,时间复杂度为 \(O(n + m)\),其中 \(n\) 是文本串长度,\(m\) 是模式串长度[^3]。 #### 二、部分匹配表(`next`数组)的构造 `next`数组记录了模式串中每个位置对应的最长公共前后缀长度。例如,对于模式串 `"ABABC"`,其`next`数组为 `[0, 0, 1, 2, 0]`。以下是构造`next`数组的步骤: ```python def compute_next(pattern): n = len(pattern) next_array = [0] * n j = 0 # 前缀末尾索引 for i in range(1, n): # 后缀末尾索引从1开始 while j > 0 and pattern[i] != pattern[j]: j = next_array[j - 1] if pattern[i] == pattern[j]: j += 1 next_array[i] = j return next_array ``` #### 三、KMP算法的实现 基于上述`next`数组,可以实现高效的字符串匹配。以下是完整的KMP算法实现代码: ```python def kmp_search(text, pattern): next_array = compute_next(pattern) # 构造next数组 n, m = len(text), len(pattern) j = 0 # 模式串指针 for i in range(n): # 遍历文本串 while j > 0 and text[i] != pattern[j]: j = next_array[j - 1] if text[i] == pattern[j]: j += 1 if j == m: # 匹配成功 return i - m + 1 # 返回匹配起始位置 return -1 # 匹配失败 ``` #### 四、示例分析 假设文本串为 `"ABABABCABABC"`, 模式串为 `"ABABC"`,则通过KMP算法可以快速找到模式串在文本串中的首次出现位置。具体过程如下: 1. 初始化文本串指针 `i = 0` 和模式串指针 `j = 0`。 2. 依次比较字符,若匹配失败,则根据`next`数组调整模式串指针 `j`。 3. 当模式串完全匹配时,返回匹配起始位置。 #### 五、C语言实现示例 除了Python,KMP算法也可以用C语言实现。以下是一个简单的C语言版本: ```c #include <stdio.h> #include <string.h> void computeNext(const char* pattern, int* next, int m) { int j = 0; next[0] = 0; for (int i = 1; i < m; i++) { while (j > 0 && pattern[i] != pattern[j]) { j = next[j - 1]; } if (pattern[i] == pattern[j]) { j++; } next[i] = j; } } int kmpSearch(const char* text, const char* pattern) { int n = strlen(text); int m = strlen(pattern); int next[m]; computeNext(pattern, next, m); int j = 0; for (int i = 0; i < n; i++) { while (j > 0 && text[i] != pattern[j]) { j = next[j - 1]; } if (text[i] == pattern[j]) { j++; } if (j == m) { return i - m + 1; } } return -1; } int main() { const char* text = "ABABABCABABC"; const char* pattern = "ABABC"; int result = kmpSearch(text, pattern); printf("Pattern found at index: %d\n", result); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值