zoukankan      html  css  js  c++  java
  • KMP&拓展KMP

    KMP算法

    说明

    KMP算法是一种比较高效的字符串匹配算法,可以在线性时间内求出一个串在另一个串的所有匹配位置。

    解析

    详解KMP

    设模板串是 (pattern)(next[i] = max{k|pattern[0...k-1]=pattern[i-k+1...i]}), (next[]) 可以通过动态规划求解。

    板子

    (next)数组存在的意义:

    (A) 串匹配到 (i)(B) 串匹配到 (j)时, 如果发现失配,可以直接令 (j = next[i]) 然后继续匹配, ( (next[i]) 将记录如果 (B) 串匹配到 (A)(i) 位置,前面有多少是已经匹配了的。)

    (next) 数组的求法

    算法流程

    1. 初始化 (next[1] = j = 0) ,假设 (next[1...i-1]) 已经求出,下面求解 (next[i])
    2. 不断尝试扩展长度 (j), 如果扩展失败(下一个字符不相等),令 (j) 变为 (next[j]), 直至 (j = 0)(应该从头开始匹配)
    3. 如果扩展成功,匹配长度 (j) 就增加 (1), (next[i]) 的值就是 (j)
    inline void calc_next() {
    	next[1] = 0;
    	for (int i = 2, j = 0; i <= n; ++ i) {
    		while (j > 0 && a[i] != a[j + 1]) j = next[j];
    		if (a[i] == a[j + 1]) j ++;
    		next[i] = j;
    	}
    }
    

    我们用(f)数组记录每个位置能匹配的个数,基于 (next) 数组,我们能在 (O(n + m)) 的时间处理结果

    inline void calc_f() {
    	for (int i = 1, j = 0; i <= m; ++ i) {
    		while (j > 0 && (j == n || b[i] != a[j + 1])) j = next[j];
    		if (b[i] == a[j + 1]) j ++;
    		f[i] = j;
    		if (f[i] == n) {/* the first time */}
    	}
    }
    

    拓展KMP算法

    说明

    在线性复杂度内求出一个串对于另一个串的每个后缀的最长公共前缀

    解析

    假设两个串是 (s)(p), 要求 (p) 的每个 (s) 的后缀的最长公共前缀.

    我们可以先求出 (p) 与它自己的每个后缀的最长公共前缀(假设为 (A))。类似KMP的思想,假设我们现在要计算 (p) 的第 (i) 个字符开头的后缀,而我们已经得到了 (A[1...i-1]), 我们可以找到以前的一个 (k) 使得 (k + A[k] - 1) 最大(就是匹配到的范围最大),我们可以得知 (p[1...A[k]] = p[k...k+A[k]-1]), 于是可以得到 (p[i...k+A[k]-1] = p[i-k+1...A[k]]),即我们可以利用 (A[i - j + 1]) 的信息。

    分两种情况讨论,如果 (i+A[i-k+1]-1)(k+A[k]-1)小,则 (A[k] = A[i - k + 1]) ,否则暴力扫一次。计算 (p)(s) 的后缀的最长公共前缀也是类似的方法,可以证明复杂度是线性的。

    板子

    (未精简版本)

    输入:求 (a) 关于 (b) 的后缀的最长公共前缀, (Next) 记录 (a)关于自己每个后缀的最长公共前缀, (ret) 记录 (a) 关于 (b) 的后缀的最长公共前缀

    void ExtendedKMP(char *a, char *b, int M, int N, int *Next, int *ret) {
    	int i, j, k;
    	for (j = 0; 1 + j < M && a[j] == a[1 + j]; ++ j);
    	Next[1] = j;
    	k = 1;
    	for (i = 2; i < M; ++ i) {
    		int Len = k + Next[k], L = Next[i - k];
    		if (L < Len - i) {
    			Next[i] = L;
    		} else {
    			for (j = max(0, Len - i); i + j < M && a[j] == a[i + j]; ++ j);
    			Next[i] = j;
    			k = i;
    		}
    	}
    	for (j = 0; j < N && j < M && a[j] == b[j]; ++ j);
    	ret[0] = j;
    	k = 0;
    	for (i = 1; i < N; ++ i) {
    		int Len = k + ret[k], L = Next[i - k];
    		if (L < Len - i) {
    			ret[i] = L;
    		} else {
    			for (j = max(0, Len - i); j < M && i + j < N && a[j] == b[i + j]; ++ j);
    			ret[i] = j;
    			k = i;
    		}
    	}
    }
    
  • 相关阅读:
    转 mysql 数据结构详解
    转单元测试之道C#版
    转 告诉你如何用C#写出iOS与Android应用
    转 MySQL索引背后的数据结构及算法原理
    转单元测试基础知识
    转C#冒泡排序
    如何让web页面鼠标右键单击之后不出现菜单选项
    开博文
    jquery ui 1.7 ui.tabs 动态添加与关闭(按钮关闭+双击关闭)
    jquery ui tabs详解(中文)
  • 原文地址:https://www.cnblogs.com/Alessandro/p/9712237.html
Copyright © 2011-2022 走看看