不知道为什么今天晚上神经病,一直睡不着,挣扎了四个多小时,还是决定起来搞点东西,就补了一题:A了之后对比了一下标程似乎更优化~~~快了6倍多代码也很短~
思路:对所有子串建立AC自动机,然后只需要定义两个数组,一个是每个节点的父亲,一个是每组字符串的最后一个字符的节点标号,然后就顺着每一个x串的fail指针去标记一下,然后顺着y串的fail去搜,搜到标记过的,就更新一下当前最大就好,然后就是结果了,复杂度O(n);
代码:
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn=1e5+10; struct Trie { int next[maxn][26],fail[maxn],f[maxn],dep[maxn],pos[maxn]; bool vis[maxn]; int root,L; int newnode() { for(int i = 0;i < 26;i++) next[L][i] = -1; L++; return L-1; } void init() { L = 0; dep[0]=0; f[0]=0; root = newnode(); } void insert(char buf[],int k) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(next[now][buf[i]-‘a‘] == -1) { next[now][buf[i]-‘a‘] = newnode(); dep[next[now][buf[i]-‘a‘]]=dep[now]+1; f[next[now][buf[i]-‘a‘]]=now; } now = next[now][buf[i]-‘a‘]; } pos[k]=now; } void build() { queue<int>q; fail[0] = 0; for(int i = 0;i < 26;i++) if(next[0][i] == -1) next[0][i] = 0; else { fail[next[0][i]] = 0; q.push(next[0][i]); } while(!q.empty()) { int now = q.front();q.pop(); for(int i = 0;i < 26;i++) { int u=next[now][i]; if(u == -1) next[now][i] = next[fail[now]][i]; else { fail[u]=next[fail[now]][i]; q.push(u); } } } } int check(int x,int y) { memset(vis,0,(L+10)*sizeof (bool)); for(int i=pos[x];i!=0;i=f[i]) for(int p=i;p!=0;p=fail[p]) vis[p]=1; int M=0; for(int i=pos[y];i!=0;i=f[i]) for(int p=i;p!=0;p=fail[p]) if(vis[p])M=max(M,dep[p]); return M; } }; char buf[100005]; Trie ac; int main() { freopen("input.txt","r",stdin); int T,n,m; scanf("%d",&T); while( T-- ) { scanf("%d",&n); ac.init(); for(int i = 1;i <= n;i++) { scanf("%s",buf); ac.insert(buf,i); } ac.build(); scanf("%d",&m); while(m--) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",ac.check(x,y)); } } return 0; }
时间: 2024-10-16 15:26:04