题目: 传送门

题意: 给你一个 n * m 的文本串 T, 再给你一个 r * c 的模式串 S;

   问模式串 S 在文本串 T 中出现了多少次。

解:

法一: AC自动机 (正解) 670ms

  把模式串的每一行当成一个字符串, 建一个AC自动机。

  然后设cnt[ x ][ y ] 表示文本串中,以 (x, y) 这个点为矩阵右上角的点,且矩阵大小为 r * c的矩阵与模式串匹配了多少行。

  那最终统计答案的时候, 只需要 o(n * m) 枚举所有点,记录那些 cnt[ x ][ y ] == n 的点 的个数。 就是答案。

  那我们建完AC自动机后, 就可以枚举文本串的每一行,让其去跑 建成的AC自动机, 记录匹配情况即可。

  文本串的第 x 行和 模式串的 第 i 行匹配, 则, cnt[ x - i + 1][ y ] ++;

  我的代码里有一个 nx[ ] 数组, 这个数组的作用是。

  若模式串中, 存在多行字符是完全相等的情况, 则你文本串和当前字符串匹配,可能有多种情况。

  比如,你模式串的第3行和第5行是完全相等的, 那么, 你要是文本串匹配到了模式串的第3行,那么你也同样匹配到了第5行。

  所以,增加nx[]数组来存,同一字符串的不同编号。

  1. #include <bits/stdc++.h>
  2. #define LL long long
  3. #define rep(i, j, k) for(int i = j; i <= k; i++)
  4. #define dep(i, j, k) for(int i = k; i >= j; i--)
  5. #define mem(i, j) memset(i, j, sizeof(i))
  6. using namespace std;
  7.  
  8. const int N = 1e3 + , M = 1e4 + ;
  9.  
  10. struct Trie {
  11. int ch[M][], val[M], Fail[M], tot, nx[M], last[M], cnt[N][N];
  12. void init() {
  13. mem(ch[], ); val[] = ; tot = ; last[] = ; mem(cnt, ); mem(nx, );
  14. }
  15. int get(char Q) {
  16. return Q - 'a';
  17. }
  18. void join(char s[], int pos) {
  19. int now = ; int len = strlen(s);
  20. rep(i, , len - ) {
  21. int id = get(s[i]);
  22. if(!ch[now][id]) {
  23. mem(ch[tot], ); val[tot] = ; last[tot] = ;
  24. ch[now][id] = tot++;
  25. }
  26. now = ch[now][id];
  27. }
  28. nx[pos] = val[now];
  29. val[now] = pos;
  30. }
  31. void getFail() {
  32. queue<int> Q; while(!Q.empty()) Q.pop();
  33. rep(i, , ) {
  34. if(ch[][i]) {
  35. Q.push(ch[][i]);
  36. Fail[ch[][i]] = ;
  37. last[ch[][i]] = ;
  38. }
  39. }
  40. while(!Q.empty()) {
  41. int now = Q.front(); Q.pop();
  42. rep(i, , ) {
  43. int u = ch[now][i];
  44. if(!ch[now][i]) ch[now][i] = ch[Fail[now]][i];
  45. else {
  46. Q.push(ch[now][i]);
  47. Fail[u] = ch[Fail[now]][i];
  48. last[u] = val[Fail[u]] ? Fail[u] : last[Fail[u]];
  49. }
  50. }
  51. }
  52. }
  53. void add_ans(int x, int y, int u) {
  54. if(u) {
  55. if(x - val[u] + >= ) {
  56. cnt[x - val[u] + ][y]++;
  57. }
  58. int tmp = val[u];
  59. while(nx[tmp]) {
  60. tmp = nx[tmp];
  61. if(x - tmp + >= ) cnt[x - tmp + ][y]++;
  62. }
  63. add_ans(x, y, last[u]);
  64. }
  65. }
  66. void print(char s[], int x) {
  67. int len = strlen(s + ); int now = ;
  68. rep(i, , len) {
  69. int id = get(s[i]);
  70. now = ch[now][id];
  71.  
  72. if(val[now]) {
  73. add_ans(x, i, now);
  74. }
  75. else if(last[now]) {
  76. add_ans(x, i, last[now]);
  77. }
  78. }
  79. }
  80. };
  81. Trie AC;
  82. char s[][], ss[];
  83. int main() {
  84. int _; scanf("%d", &_);
  85. while(_--) {
  86. AC.init();
  87. int n, m; scanf("%d %d", &n, &m);
  88. rep(i, , n) scanf("%s", s[i] + );
  89. int r, c; scanf("%d %d", &r, &c);
  90. rep(i, , r) {
  91. scanf("%s", ss); AC.join(ss, i);
  92. }
  93. AC.getFail(); ///建AC自动机
  94. rep(i, , n) { /// 对文本串每一行跑AC自动机,记录匹配情况
  95. AC.print(s[i], i);
  96. }
  97. int ans = ;
  98. rep(i, , n) rep(j, , m) {
  99. if(AC.cnt[i][j] == r) ans++;
  100. }
  101. printf("%d\n", ans);
  102. }
  103. return ;
  104. }

法二: 二维Hash 40ms

  参考: 博客

  

  1. #include <bits/stdc++.h>
  2. #define LL long long
  3. #define ULL unsigned long long
  4. #define rep(i, j, k) for(int i = j; i <= k; i++)
  5. #define dep(i, j, k) for(int i = k; i >= j; i--)
  6. #define mem(i, j) memset(i, j, sizeof(i))
  7. using namespace std;
  8. const int N = 1e3 + ;
  9. const unsigned int hash1 = 1e9 + , hash2 = 1e9 + ;
  10. char a[N][N], b[][];
  11. unsigned int p1[N], p2[N];
  12. unsigned int hs[N][N];
  13. void init() {
  14. p1[] = ; p2[] = ;
  15. rep(i, , N - ) p1[i] = p1[i - ] * hash1, p2[i] = p2[i - ]* hash2;
  16. }
  17. int main() {
  18. int _; scanf("%d", &_); init();
  19. while(_--) {
  20. int n, m; scanf("%d %d", &n, &m);
  21. rep(i, , n) scanf("%s", a[i] + );
  22. int x, y; scanf("%d %d", &x, &y);
  23. rep(i, , x) scanf("%s", b[i] + );
  24. rep(i, , n) rep(j, , m) { /// 预处理n * m矩阵的二维前缀和。(三个方向的前缀和,)
  25. hs[i][j] = hs[i - ][j - ] * hash1 * hash2 + (hs[i - ][j] - hs[i - ][j - ] * hash2) * hash1 + (hs[i][j - ] - hs[i - ][j - ] * hash1) * hash2 + a[i][j];
  26. }
  27. unsigned int S = , C;
  28. rep(i, , x) rep(j, , y) { ///算模式串的hash值
  29. S += b[i][j] * p1[x - i] * p2[y - j];
  30. }
  31. int ans = ;
  32. rep(i, x, n) rep(j, y, m) { ///枚举文本串所有x*y矩阵,o(1)算出它们的hash值
  33. C = hs[i][j] - hs[i - x][j - y] * p1[x] * p2[y] - (hs[i][j - y] - hs[i - x][j - y] * p1[x]) * p2[y] - (hs[i - x][j] - hs[i - x][j - y] * p2[y]) * p1[x];
  34. if(S == C) ans++;
  35. }
  36. printf("%d\n", ans);
  37. }
  38. return ;
  39. }

UVA 11019 Matrix Matcher ( 二维字符串匹配, AC自动机 || 二维Hash )的更多相关文章

  1. UVA 11019 Matrix Matcher 矩阵匹配器 AC自动机 二维文本串查找二维模式串

    链接:https://vjudge.net/problem/UVA-11019lrjP218 matrix matcher #include<bits/stdc++.h> using na ...

  2. UVA - 11019 Matrix Matcher (二维字符串哈希)

    给你一个n*m的矩阵,和一个x*y的模式矩阵,求模式矩阵在原矩阵中的出现次数. 看上去是kmp在二维情况下的版本,但单纯的kmp已经无法做到了,所以考虑字符串哈希. 类比一维情况下的哈希算法,利用容斥 ...

  3. UVa 11019 Matrix Matcher - Hash

    题目传送门 快速的vjudge传送门 快速的UVa传送门 题目大意 给定两个矩阵S和T,问T在S中出现了多少次. 不会AC自动机做法. 考虑一维的字符串Hash怎么做. 对于一个长度为$l$的字符串$ ...

  4. uva 11019 Matrix Matcher

    题意:给出一个n*m的字符矩阵T,你的任务是找出给定的x*y的字符矩阵P在T中出现了多少次. 思路:要想整个矩阵匹配,至少各行都得匹配.所以先把P的每行看做一个模式串构造出AC自动机,然后在T中的各行 ...

  5. UVA 11019 Matrix Matcher(ac自动机)

    题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  6. UVA 11019 Matrix Matcher(二维hash + 尺取)题解

    题意:在n*m方格中找有几个x*y矩阵. 思路:二维hash,总体思路和一维差不太多,先把每行hash,变成一维的数组,再对这个一维数组hash变成二维hash.之前还在想怎么快速把一个矩阵的hash ...

  7. AC自动机(二维) UVA 11019 Matrix Matcher

    题目传送门 题意:训练指南P218 分析:一行一行的插入,一行一行的匹配,当匹配成功时将对应子矩阵的左上角位置cnt[r][c]++;然后统计 cnt[r][c] == x 的数量 #include ...

  8. UVA 11019 Matrix Matcher(哈希)

    题意 给定一个 \(n\times m\) 的矩阵,在给定一个 \(x\times y\) 的小矩阵,求小矩阵在大矩阵中出现的次数. \(1 \leq n,m \leq 1000\) \(1\leq ...

  9. UVA - 11019 Matrix Matcher hash+KMP

    题目链接:传送门 题解: 枚举每一行,每一行当中连续的y个我们hash 出来 那么一行就是 m - y + 1个hash值,形成的一个新 矩阵 大小是 n*(m - y + 1), 我们要找到x*y这 ...

随机推荐

  1. 函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!闭包访问局部变量

    函数内部声明变量的时候,一定要使用var命令.如果不用的话,你实际上声明了一个全局变量! function f1(){ n=999; } f1(); alert(n); 子函数可以一层一层读取到父元素 ...

  2. mysql中常见正则表达式的应用

    查找name字段中以'st'为开头的所有数据: mysql> SELECT name FROM person_tbl WHERE name REGEXP '^st'; 查找name字段中以'ok ...

  3. tkinter python(图形开发界面) 转自:渔单渠

    Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口.Tk和Tkinter可以在大多数的Unix平台下使用,同样可以应用在Windows和Macinto ...

  4. 利用脚本一键执行脚本,创建SharePoint文档库列表

    SharePoint基于文档库和列表上进行二次开发,生成新的文档库和新的列表模板 通过新的模板,创建新的文档库与列表 --定义site对象$site = SPSite http://dvt176/si ...

  5. kubernetes第九章--管理机密信息

  6. 四 python中关于OOP的常用术语

    抽象/实现 抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型.抽象不仅包括这种模型的数据属性,还定义了这些数据的接口. 对某种抽象的实现 ...

  7. JavaScript,数组,方法;

    JavaScript,数组除了常见的增删查改,其他的方法也有很多. let arr = [1, 3, 7, 6, 9, 4, 6, 2]; 1:every方法, 对数组进行遍历,每个元素都满足条件时, ...

  8. JavaScript的深浅复制

    JavaScript的深浅复制 为什么有深复制.浅复制? JavaScript中有两种数据类型,基本数据类型如undefined.null.boolean.number.string,另一类是Obje ...

  9. React Native 开发豆瓣评分(六)添加字体图标

    添加依赖 yarn add react-native-vector-icons Link 依赖 react-native link react-native-vector-icons 使用默认字体图标 ...

  10. stm32 USART_IT_IDLE中断 一帧数据

    USART_IT_IDLE中断,是串口收到一帧数据后,发生的中断.也可以叫做一包数据 USART_IT_IDLE和USART_IT_RXNE区别 当接收到1个字节,会产生USART_IT_RXNE中断 ...