题目:
给一个字母矩阵和几个模式串,矩阵中的字符串可以有8个方向
输出每个模式串开头在矩阵中出现的坐标和这个串的方向
题解:
我们可以把模式串搞成AC自动机,然后枚举矩阵最外围一层的每个字母,向八个方向进行匹配
代码中danger标记为判断这个节点是不是一个模式串的结尾,
这道题可以直接字符串反向构建AC自动机,匹配到的就是开头(代码是正向)
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define Z 27 #define N 1010 using namespace std; struct node { int trans[Z],fail,danger; void init() { memset(trans,0,sizeof(trans)); danger=fail=0; } }tr[1000010]; char puzzle[N][N],s[N],dir[10]={'A','B','C','D','E','F','G','H'}; int l,c,w,dx[10]={-1,-1,0,1,1,1,0,-1},dy[10]={0,1,1,1,0,-1,-1,-1}; int fa[N],ansx[N],ansy[N],tot=1,len[N],d[N]; int find(int x) { return fa[x]=fa[x]==x?x:find(fa[x]); } void insert(char s[],int id) { int len=strlen(s+1),now=1; for (int i=1;i<=len;i++) { if (tr[now].trans[s[i]-'A']==0) tr[tr[now].trans[s[i]-'A']=++tot].init(); now=tr[now].trans[s[i]-'A']; } if (tr[now].danger) fa[find(id)]=find(tr[now].danger); else tr[now].danger=id; } void BuildFail() { queue <int> q; tr[1].fail=1; for (int i=0;i<26;i++) { if (tr[1].trans[i]==0) tr[1].trans[i]=1; else { tr[tr[1].trans[i]].fail=1; q.push(tr[1].trans[i]); } } while (!q.empty()) { int u=q.front(),v,w; for (int i=0;i<26;i++) { v=tr[u].fail; v=tr[v].trans[i],w=tr[u].trans[i]; if (w) tr[w].fail=v,q.push(w); else tr[u].trans[i]=v; } q.pop(); if (tr[u].danger==0 && tr[tr[u].fail].danger) tr[u].danger=tr[tr[u].fail].danger; } } void query(int x,int y,int t) { int p=1; while (x>0 && y>0 && x<=l && y<=c) { p=tr[p].trans[puzzle[x][y]-'A']; int ap=p; while (tr[ap].danger) { int k=tr[ap].danger; ansx[k]=x-dx[t]*(len[k]-1); ansy[k]=y-dy[t]*(len[k]-1); d[k]=t; ap=tr[ap].fail; } x+=dx[t]; y+=dy[t]; } } int main() { scanf("%d%d%d",&l,&c,&w); for (int i=1;i<=l;i++) scanf("%s",puzzle[i]+1); for (int i=1;i<=w;i++) { scanf("%s",s+1); fa[i]=i; len[i]=strlen(s+1); insert(s,i); } BuildFail(); for (int i=1;i<=l;i++) for (int j=0;j<8;j++) query(i,1,j),query(i,c,j); for (int i=1;i<=c;i++) for (int j=0;j<8;j++) query(1,i,j),query(l,i,j); for (int i=1;i<=w;i++) printf("%d %d %c ",ansx[i]-1,ansy[i]-1,dir[d[i]]); return 0; }