本文着重讨论由Rabin-Karp算法推广到二维来解决二维模式匹配问题的算法。

问题:
在一个n1*n2的二维字符组成中搜寻一个给定的m1*m2的模式。参考《算法导论》习题32.2-3.

分析:
1. 首先简单介绍一下Rabin-Karp算法
Rabin-Karp算法是一种字符串匹配算法,它的主要思想是预先计算出模式串的hash值,匹配时再计算出待匹配子串的hash值,直接比较模式串和当前子串的hash值是否相等即可判断是否匹配。

为了便于说明,以下以数字串为例(字符串的每个字符都是一个十进制的数字,比如字符串31415)。已知一个模式P[1..m],设p表示其相应的 十进制数的值。类似的,对于给定的文本T[1..n],用ts表示其长度为m的子字符串T[s+1..s+m](s=0,1,..,n-m)相应的十进制 数的值。很显然的,如果T[s+1..s+m]与模式P[1..m]匹配,那么ts一定等于p,相反的,如果ts=p,那么T[s+1..s+m]一定与P[1..m]匹配。如果能够在O(m)时间内计算出p的值,并在总共O(n-m+1)的时间内计算出所有ts的值,那么通过把p值与每个ts值进行比较,就能够在O(m)+O(n-m+1)=O(n)的时间内,找到所有的匹配。

RK算法通过霍纳法则(Horner’s Rule)在O(m)时间内计算出p的值:

p=P[m]+10(P[m-1]+10(P[m-2]+…+10(P[2]+10P[1])..))

同理可以在O(m)时间内计算出t0的值。

又因为

ts+1=10[ts-10m-1T[s+1])+T[s+m+1]

所以可以在常数时间内根据ts计算出ts+1

例如,如果m=5,ts=12345,假定下一位是6,去掉高位数字1,再加入低位数字6,就得到:

ts+1=10*(12345-10000*1)+6=23456

因此,Rabin-Karp算法能够用O(m)的预处理时间和O(n-m+1)的匹配时间,找出模式P[1..m]在文本T[1..n]中的所有出现。

这个过程唯一的问题是p和ts的值可能太大,RK算法是通过模等价来处理。实际计算出来的p和ts的值都是模q后的值,在匹配时如果p和ts值相等,串还未必匹配,这时候还需要通过朴素的比较这两个串来测试。本文不考虑这个,有兴趣的可以参考算法导论。

另外,这个例子是基于数字串的,对于一般情况,可以假定每个字符都是基数为d的表示法中的一个字符,这时上面2个计算公式中的10要换成相应的d。

2. 推广到二维
首先,来看模式矩阵。如果把m2列中的每一列都看做一个整体,那么他们每一个都是一个一维的串,可以分别计算出hash值(使用霍纳法则),这样模式矩阵就成了一个一维的长度为m2的模式串。

然后,对大矩阵的前m1行,用同样的方法能得到一个长度为n2的串。

这样,在大矩阵的前m1行中寻找模式矩阵,就转化成了一维的字符串匹配问题。(这里使用一维的串匹配算法就能解决,比如KMP)

最后,用同样的方法,在大矩阵的第2到第m1+1行,第3到m1+2行。。。都可以用同样的方法匹配。
这里的关键是,每次匹配时,转化后的一维串可以通过上次的串直接计算出来。(类似于Rabin-Karp由ts可以在常数时间内计算出ts+1

源码- JAVA

01    public class StringMatch2D {02     03        public static void main(String[] args) {04            char[][] text = {05                               { 'a', 'b', 'a', 'b', 'a' },06                               { 'a', 'b', 'a', 'b', 'a' },07                               { 'a', 'b', 'b', 'a', 'a' },08                               { 'a', 'b', 'a', 'a', 'b' },09                               { 'b', 'b', 'a', 'b', 'a' }10                             };11            char[][] pattern = {12                           { 'a', 'b' },13                           { 'b', 'a' }14                             };15     16            matrixPatternMatch(text, pattern);17        }18     19        private static void matrixPatternMatch(char[][] text, char[][] pattern) {20            // pre-process21            int[] patternStamp = new int[pattern[0].length];22            int[] textStamp = new int1.length];23     24            caculateStamp(pattern, pattern.length, patternStamp);25            caculateStamp(text, pattern.length, textStamp);26     27            int[] next = new int[patternStamp.length];28            caculateNext(patternStamp, next);29     30            for (int i = 0; i < (text.length - pattern.length + 1); i++) {31                int col = isMatch(patternStamp, textStamp, next);32                if (col != -1) {33                    System.out.println("found");34                    System.out.println(i+", "+col);35                }36     37                // move down38                if(i < text.length - pattern.length)39                    caculateNextStamp(text, pattern.length, textStamp, i);40            }41     42        }43     44        private static int isMatch(int[] patternStamp, int[] textStamp, int[] next) {45            int i = 0, j = 0;46            while (j < patternStamp.length && i < textStamp.length) {47                if (j == -1 || patternStamp[j] == textStamp[i]) {48                    i++;49                    j++;50                } else {51                    j = next[j];52                }53            }54     55            if (j == patternStamp.length) {56                return i-j;57            } else {58                return -1;59            }60        }61     62        private static void caculateNext(int[] pattern, int[] next) {63            next[0] = -1;64     65            int i = 0, j = -1;66            while(i<pattern.length-1) {67                if(j==-1 || pattern[i] == pattern[j]) {68                    i++;69                    j++;70                    next[i] = j;71                } else {72                    j = next[j];73                }74            }75     76        }77     78        private static void caculateNextStamp(char[][] text, int height,79                int[] textStamp, int row) {80            int d = (int) Math.pow(26, height-1);81            for (int i = 0; i < textStamp.length; i++) {82                textStamp[i] = 26 * (textStamp[i] - d * text[row][i]) + text[row + height][i];83            }84        }85     86        private static void caculateStamp(char[][] input, int height, int[] result) {87            for (int i = 0; i < result.length; i++) {88                result[i] = 0;89                for (int j = 0; j < height; j++) {90                    result[i] = 26 * result[i] + input[j][i];91                }92            }93        }94     95    }

21-28行,进行匹配前的预处理。
21-22行,patternStamp和textStamp分别用来存储模式矩阵以及文本矩阵的前m1行转换成一维数字串。
86-93行,定义函数caculateStamp,输入是一个二维字符矩阵,输出是转换成的一维数字串。对输入矩阵的每一列用霍纳法则计算出一个值,作为转换后的一维数字串的一位。
24-25行,用函数caculateStamp计算得到转换后的一维数字串。
27-28行,计算转换后的一维模式串的next数组,用于KMP算法进行一维串匹配。

30-40行,进行实际的匹配。
匹配时,对文本矩阵每m1行进行一次匹配,匹配前都转换成一维的数字串,用KMP算法(第31行)进行一维串匹配,总共要匹配n1-m1+1次。
38-39行,待匹配的文本矩阵下移一行,计算转换成的新一维数字串。
78-84行,函数caculateNextStamp,用来根据上一次的一维转换数字串计算出新的,类似于RK算法中的公式二。

时间复杂度
预处理 – O(n2*m1)
计算模式矩阵的stamp O(m1*m2) + 计算文本矩阵前m1行的stamp O(n2*m1) + 计算模式矩阵stamp的next数组 O(m2)

匹配 – O((n1-m1+1)*n2)
总共 n1-m1+1次
每次用KMP算法匹配 O(n2) + 计算文本矩阵下m行的stamp O(n2)

总的时间复杂度 – O(n1*n2)

[转]: http://www.slimeden.com/2010/10/algorithm/matrixpatternmatching

二维模式(矩阵)匹配(Rabin-Karp算法推广到二维)[转]相关推荐

  1. Rabin Karp 算法详解及Python实现

    目录 一.Rabin Karp 核心思路 二.字符串如何做哈希映射 三.借助前缀和列表计算滑动窗口 四.leetcode28. 代码实现 Rabin Karp 算法是用于实现字符串的模式匹配,先看le ...

  2. C++Rabin Karp算法字符串快速查找(附完整源码)

    C++Rabin Karp算法字符串快速查找 C++Rabin Karp算法字符串快速查找完整源码(定义,实现,main函数测试) C++Rabin Karp算法字符串快速查找完整源码(定义,实现,m ...

  3. 基于矩阵分解的CF算法实现(二):BiasSvd

    基于矩阵分解的CF算法实现(二):BiasSvd BiasSvd其实就是前面提到的Funk SVD矩阵分解基础上加上了偏置项. BiasSvd 利用BiasSvd预测用户对物品的评分, k k k表示 ...

  4. leetcode 1044. Longest Duplicate Substring | 1044. 最长重复子串(Rabin Karp算法)

    题目 https://leetcode.com/problems/longest-duplicate-substring/ 题解 这题暴力超时,看了 Related Topics,以及 Hint,主要 ...

  5. SPOJ Substring Problem(Rabin Karp TLE)

    给出一个文本串及n个模式串,检查对应的模式串是否在文本串中出现 使用rabin karp算法超时 代码见: https://github.com/wuli2496/OJ/blob/master/spo ...

  6. gabor 幅值域 matlab,基于全局二值模式的特征提取方法及其应用

    基于全局二值模式的特征提取方法及其应用 来源:原创论文网 添加时间:2014-02-09 摘 要 提出一种全局二值模式( GBP) 的纹理分析方法,解决局部二值模式( LBP) 易受噪声影响的问题. ...

  7. C++旋转二维MxN矩阵的算法(附完整源码)

    C++旋转二维MxN矩阵的算法 C++旋转二维MxN矩阵的算法完整源码(定义,实现,main函数测试) C++旋转二维MxN矩阵的算法完整源码(定义,实现,main函数测试) #include < ...

  8. 矩阵(二维数组)的性质在算法求解中的应用

    本文所说的矩阵(matrix),其实在编程实现时,往往以二维数组的形式出现. 1. 对称矩阵(二维数组) 在求解旅行商问题时,题干中要求,城市之间彼此互通(两城市之间的道路只有一条). double ...

  9. 数据结构与算法Java(二)——字符串、矩阵压缩、递归、动态规划

    不定期补充.修正.更新:欢迎大家讨论和指正 本文以数据结构(C语言版)第三版 李云清 杨庆红编著为主要参考资料,用Java来实现 数据结构与算法Java(一)--线性表 数据结构与算法Java(二)- ...

最新文章

  1. 软件工程-第三次作业
  2. js实现随机选取[10,100)中的10个整数,存入一个数组,并排序。 另考虑(10,100]和[10,100]两种情况。...
  3. 记一次lwip中 遇到 pcb == pcb-next 的pcb死循环debug过程
  4. eclipse启动tomcat 404
  5. varnish服务器在内存大量富余时使用交换空间的原因及解决方法
  6. c post请求网页_Python使用urllib2抓取网页
  7. Flink : Cannot find compatible factory for specified execution.target (=local)
  8. HTTP协议···(一)
  9. 1500个前端开发常用JavaScript特效
  10. android.net.http.AndroidHttpClient Android6.0 API23以后失效
  11. android开源库合集
  12. Redis 的过期策略都有哪些?
  13. Kmeans算法思想
  14. linux 查找mysql rpm包位置_linux如何查看rpm包的安装路径办法
  15. Django项目中常用的配置与官方文档
  16. 后台弹出界面权限踩坑
  17. 旅游网站首页——html
  18. win server 2008 r2 iis+php 500错误内部服务器错误。
  19. 7、邮箱验证码的功能设计
  20. Jenkins-Pipline

热门文章

  1. 【技术综述】一文道尽R-CNN系列目标检测
  2. 中国工业自动化行业需求现状及投资风险评估报告2022-2027年版
  3. python程序基础书写文本_Python基础手册4——文本结构
  4. Selenium+python --获取百度联想词
  5. 使用 vue + thinkjs 开发博客程序记录
  6. 泛 归并排序 及 逆序对
  7. 解析利用wsdl.exe生成webservice代理类的详解
  8. 安装Ruby、Sass在WebStrom添加Watcher实现编辑scss文件时自动生成.map和压缩后的.css文件...
  9. 基于线性表邻接矩阵结构的图的深度/广度优先搜索算法
  10. Python核心编程读笔 8: 文件和输入输出