首页 > 二维模式(矩阵)匹配(Rabin-Karp算法推广到二维)[转]

二维模式(矩阵)匹配(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-process

21 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 down

38 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(i1) {

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

更多相关:

  • 现在,WEB系统的开发一般都采用前后端分离的架构,以及部分公司采用“前台-中台-后台“的组织架构,难免会出现开发进度不一致的情况,导致系统联调或测试需要等到所有依赖开发完成后才能够进行,为不影响软件开发、测试进度,消除等待浪费,因此引入了Mock服务。本文主要介绍的Mock工具是Wiremock(一种开源的测试工具,Mock工具有很多...

  • UE商城资源 Motion Symphony 运动匹配插件 Unreal Engine虚幻游戏引擎素材资源 Unreal Engine Marketplace –Motion Symphony 1.05 4.26运动交响曲插件 插件大小解压后:346M 资源大小共 2G 含官方文档 和官方使用视频教程(共100分钟 1920X...

  • 原文地址:http://www.cnblogs.com/chengmo/archive/2010/10/10/1847287.html 正则表达式:在计算机科学中,是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。在很多文本编辑器或其他工具里,正则表达式通常被用来检索和/或替换那些符合某个模式的文本内容。许多程序设...

  • MQTT topic匹配规则 原文连接: https://blog.csdn.net/JiangCheng817/article/details/81333893 内容: 主题层级分隔符 “/”: 表示层级关系 单层通配符 “+”: 订阅消息时使用,匹配一层主题如 a/+ 匹配诸如 a/b a/c 但是不能匹配 a/b/c,特...

  •  1.特殊字符 ^匹配输入字符串的开始位置$匹配输入字符串的结尾位置( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。+匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。?匹配前面的子表达式零次或一次...

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • 看龙书的时候发现一个矩阵在传入Shader之前都要转置一下,很好奇为什么要有一步这样的操作。行主序和列主序行主序指矩阵在内存中逐行存储,列主序指矩阵在内存中逐列存储。行主序矩阵内存布局:列主序矩阵内存布局:行向量和列向量行向量指的是把向量当成一个一行n列的矩阵,列向量指的是把向量当成一个n行一列的矩阵。左乘和右乘矩阵“左乘”:矩阵和向...

  • ORB-SLAM点云地图中相机的位姿初始化,无论算法工作在平面场景,还是非平面场景下,都能够完成初始化的工作。其中主要是使用了适用于平面场景的单应性矩阵H和适用于非平面场景的基础矩阵F,程序中通过一个评分规则来选择适合的模型,恢复相机的旋转矩阵R和平移矩阵t那么下面主要讲解关于对极几何中的基础矩阵,本质矩阵,和单应矩阵之间的区别与联...

  • 矩阵可分为稠密矩阵和稀疏矩阵,对于稀疏矩阵而言,使用同样的内存来存储这个矩阵显然是对内存的浪费,那么我们就可以想办法将矩阵中所有的o元素挥着不相关元素剔除,怎么剔除,第一种方法是通过三个一维矩阵来存储原二维矩阵中的所有非0元素,三个矩阵分别为value、column、row, value 数组存储所有的非零元素, column 数...

  • void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; m – 目标矩阵。如果m在运算前没有合适的尺寸或类型,将被重新分配。rtype – 目标矩阵的类型。因为目标矩阵的通道数与源矩阵一样,所以rtype也可以看做是目标...

  • https://blog.csdn.net/jiangdf/article/details/8460012 glMatrixMode()函数的参数,这个函数其实就是对接下来要做什么进行一下声明,也就是在要做下一步之前告诉计算机我要对“什么”进行操作了,这个“什么”在glMatrixMode的“()”里的选项(参数)有3种模式: GL...