UVA 11019 Matrix Matcher( 二维字符匹配 AC自动机 + DP)
2013-10-18 22:41
417 查看
题目大意:给你n*m的一个字符T矩阵,和一个x*y的字符P矩阵,让你找出P矩阵在T矩阵中出现了几次。
思路:把P矩阵一行一行插进去AC自动机,设 cc[ i ][ j ] 表示以T矩阵的第 i 行,第 j 列为左上顶点与P矩阵最多的匹配行数,
那么如果在匹配过程中匹配到的字符是P的某一行末尾的字符,那么就在对应的左上顶点的 cc ++ 即可。最后统计一遍所有顶点的cc,
cc == x 的数量即为匹配次数。这里还有一点需要注意,由于构造AC自动机时,如果两个字符串相同,后面的字符串会覆盖掉前面的,
也就是说 trie 中某个节点可能对应几个P中字符串的结尾,所以这里我直接用 vector 来存 val 信息了。
这题说实话这题自己想的时候,没有思路,看了书上的思路才写的,发现AC自动机好多都和DP结合起来,这题赞一个!
错了两次才AC的,第一遍是RE,可能是由于我把变量都开在 struct 里的原因,以后干脆直接就放外面好了,之后 WA 了,然后对照模板,发现原来是
get_fail 那里的 while 我把 !ch[ pre ][ i ] 写成 ch[ pre ][ i ]了,又是小细节问题。幸好改过来后就直接A了。。 = =
代码如下:
思路:把P矩阵一行一行插进去AC自动机,设 cc[ i ][ j ] 表示以T矩阵的第 i 行,第 j 列为左上顶点与P矩阵最多的匹配行数,
那么如果在匹配过程中匹配到的字符是P的某一行末尾的字符,那么就在对应的左上顶点的 cc ++ 即可。最后统计一遍所有顶点的cc,
cc == x 的数量即为匹配次数。这里还有一点需要注意,由于构造AC自动机时,如果两个字符串相同,后面的字符串会覆盖掉前面的,
也就是说 trie 中某个节点可能对应几个P中字符串的结尾,所以这里我直接用 vector 来存 val 信息了。
这题说实话这题自己想的时候,没有思路,看了书上的思路才写的,发现AC自动机好多都和DP结合起来,这题赞一个!
错了两次才AC的,第一遍是RE,可能是由于我把变量都开在 struct 里的原因,以后干脆直接就放外面好了,之后 WA 了,然后对照模板,发现原来是
get_fail 那里的 while 我把 !ch[ pre ][ i ] 写成 ch[ pre ][ i ]了,又是小细节问题。幸好改过来后就直接A了。。 = =
代码如下:
#include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> using namespace std; const int MAX_NODE = 11111; const int SIGMA_SIZE = 26; char t[1111][1111],p[111][111]; int cc[1111][1111]; int ch[MAX_NODE][SIGMA_SIZE]; int fail[MAX_NODE]; int tot; vector <int> val[MAX_NODE]; struct Ac { void init() { memset(ch[0],0,sizeof(ch[0])); tot = 1; val[0].clear(); } int idx(char c) { return c - 'a'; } void insert(char *s,int len,int id) { int u = 0; for(int i = 0;i<len;i++) { int c = idx(s[i]); if(!ch[u][c]) { memset(ch[tot],0,sizeof(ch[tot])); val[tot].clear(); ch[u][c] = tot++; } u = ch[u][c]; } val[u].push_back(id); } void get_fail() { fail[0] = 0; queue <int> q; for(int i = 0;i<SIGMA_SIZE;i++) if(ch[0][i]) { fail[ch[0][i]] = 0; q.push(ch[0][i]); } while(!q.empty()) { int u = q.front(); q.pop(); for(int i = 0;i<SIGMA_SIZE;i++) { int v = ch[u][i]; if(!v) { ch[u][i] = ch[fail[u]][i]; continue; } q.push(v); int j = fail[u]; while(j && !ch[j][i]) j = fail[j]; j = ch[j][i]; fail[v] = j; } } } int find(int x,int y,int n,int m) { memset(cc,0,sizeof(cc)); int pre = 0; for(int i = 1;i<=n;i++) for(int j = 0;j<m;j++) { int c = idx(t[i][j]); pre = ch[pre][c]; for(int pos = 0;pos < val[pre].size();pos ++) { int ii = i - (val[pre][pos] - 1); int jj = j - (y - 1); cc[ii][jj]++; } } int ans = 0; for(int i = 1;i<=n;i++) for(int j = 0;j<m;j++) if(cc[i][j] == x) ans ++; return ans; } }ac; int main() { int _; scanf("%d",&_); while(_--) { ac.init(); int n,m; scanf("%d%d",&n,&m); for(int i = 1;i<=n;i++) scanf("%s",t[i]); int x,y; scanf("%d%d",&x,&y); for(int i = 1;i<=x;i++) { scanf("%s",p[i]); ac.insert(p[i],y,i); } ac.get_fail(); printf("%d\n",ac.find(x,y,n,m)); } return 0; }
相关文章推荐
- 抓取dump文件
- ubuntu查找命令集
- 进程nice值曲线图
- SQLServer中登录名的用户名配置
- 重新校正节点运行的实例
- OCP-1Z0-051-V9.02-88题
- 反转数字
- A*寻路算法
- 对云计算时代软件技术发展的若干思考和实践和软件工程技术思索 读后感
- 【重点】买票程序——好用的Runnable
- Java集合中那些类是线程安全的
- Linux如何查找文件安装路径?
- 实验4:在分支循环结构中调用自定义函数
- Matlab函数——fftshift
- TranslateAnimation详解
- 保护密码的利器:Ubuntu 之 KeePassX
- kernel中遍历所有进程
- 数组名 vs 指针
- 杨氏矩阵找数
- linux系统下将php和mysql命令加入到环境变量中的方法