二维模式(矩阵)匹配(Rabin-Karp算法推广到二维)[转]
本文着重讨论由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算法推广到二维)[转]相关推荐
- Rabin Karp 算法详解及Python实现
目录 一.Rabin Karp 核心思路 二.字符串如何做哈希映射 三.借助前缀和列表计算滑动窗口 四.leetcode28. 代码实现 Rabin Karp 算法是用于实现字符串的模式匹配,先看le ...
- C++Rabin Karp算法字符串快速查找(附完整源码)
C++Rabin Karp算法字符串快速查找 C++Rabin Karp算法字符串快速查找完整源码(定义,实现,main函数测试) C++Rabin Karp算法字符串快速查找完整源码(定义,实现,m ...
- 基于矩阵分解的CF算法实现(二):BiasSvd
基于矩阵分解的CF算法实现(二):BiasSvd BiasSvd其实就是前面提到的Funk SVD矩阵分解基础上加上了偏置项. BiasSvd 利用BiasSvd预测用户对物品的评分, k k k表示 ...
- leetcode 1044. Longest Duplicate Substring | 1044. 最长重复子串(Rabin Karp算法)
题目 https://leetcode.com/problems/longest-duplicate-substring/ 题解 这题暴力超时,看了 Related Topics,以及 Hint,主要 ...
- SPOJ Substring Problem(Rabin Karp TLE)
给出一个文本串及n个模式串,检查对应的模式串是否在文本串中出现 使用rabin karp算法超时 代码见: https://github.com/wuli2496/OJ/blob/master/spo ...
- gabor 幅值域 matlab,基于全局二值模式的特征提取方法及其应用
基于全局二值模式的特征提取方法及其应用 来源:原创论文网 添加时间:2014-02-09 摘 要 提出一种全局二值模式( GBP) 的纹理分析方法,解决局部二值模式( LBP) 易受噪声影响的问题. ...
- C++旋转二维MxN矩阵的算法(附完整源码)
C++旋转二维MxN矩阵的算法 C++旋转二维MxN矩阵的算法完整源码(定义,实现,main函数测试) C++旋转二维MxN矩阵的算法完整源码(定义,实现,main函数测试) #include < ...
- 矩阵(二维数组)的性质在算法求解中的应用
本文所说的矩阵(matrix),其实在编程实现时,往往以二维数组的形式出现. 1. 对称矩阵(二维数组) 在求解旅行商问题时,题干中要求,城市之间彼此互通(两城市之间的道路只有一条). double ...
- 数据结构与算法Java(二)——字符串、矩阵压缩、递归、动态规划
不定期补充.修正.更新:欢迎大家讨论和指正 本文以数据结构(C语言版)第三版 李云清 杨庆红编著为主要参考资料,用Java来实现 数据结构与算法Java(一)--线性表 数据结构与算法Java(二)- ...
最新文章
- 软件工程-第三次作业
- js实现随机选取[10,100)中的10个整数,存入一个数组,并排序。 另考虑(10,100]和[10,100]两种情况。...
- 记一次lwip中 遇到 pcb == pcb-next 的pcb死循环debug过程
- eclipse启动tomcat 404
- varnish服务器在内存大量富余时使用交换空间的原因及解决方法
- c post请求网页_Python使用urllib2抓取网页
- Flink : Cannot find compatible factory for specified execution.target (=local)
- HTTP协议···(一)
- 1500个前端开发常用JavaScript特效
- android.net.http.AndroidHttpClient Android6.0 API23以后失效
- android开源库合集
- Redis 的过期策略都有哪些?
- Kmeans算法思想
- linux 查找mysql rpm包位置_linux如何查看rpm包的安装路径办法
- Django项目中常用的配置与官方文档
- 后台弹出界面权限踩坑
- 旅游网站首页——html
- win server 2008 r2 iis+php 500错误内部服务器错误。
- 7、邮箱验证码的功能设计
- Jenkins-Pipline
热门文章
- 【技术综述】一文道尽R-CNN系列目标检测
- 中国工业自动化行业需求现状及投资风险评估报告2022-2027年版
- python程序基础书写文本_Python基础手册4——文本结构
- Selenium+python --获取百度联想词
- 使用 vue + thinkjs 开发博客程序记录
- 泛 归并排序 及 逆序对
- 解析利用wsdl.exe生成webservice代理类的详解
- 安装Ruby、Sass在WebStrom添加Watcher实现编辑scss文件时自动生成.map和压缩后的.css文件...
- 基于线性表邻接矩阵结构的图的深度/广度优先搜索算法
- Python核心编程读笔 8: 文件和输入输出