题目大意:
给你一个长度为
n
n
n的模板串。
q
q
q个询问,每个询问给一个长度为
m
m
m的字符串,求最长的前缀能在模板串中匹配。
n
≤
1
0
7
,
q
≤
1
0
5
,
m
≤
100
n≤10^7,q≤10^5,m≤100
n≤107,q≤105,m≤100。
分析:
对每一个串来匹配模板串是
O
(
n
q
)
O(nq)
O(nq)的,显然不可行。
考虑用模板串来匹配询问串。对所有询问串建AC自动机,然后把模板串拿上去跑。每走到一个点就把它的fail链打上标记,如果遇到标记可以直接break。显然每个点只会被标记一次。答案就是字典上的对于这个串的链上最长标记节点。
代码:
/**************************************************************
Problem: 4327
User: ypxrain
Language: C++
Result: Accepted
Time:3952 ms
Memory:323948 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>
const int maxn=1e7+7;
const int maxq=1e5+7;
using namespace std;
int n,m,len,cnt,ans;
int num[maxq];
char s[maxn],Q[107];
queue <int> q;
struct node{
int son[4],fa,fail,len;
bool vis;
}t[maxn];
int get(char ch)
{
if (ch=='E') return 0;
if (ch=='S') return 1;
if (ch=='W') return 2;
if (ch=='N') return 3;
}
void ins(char *s,int d)
{
int len=strlen(s+1);
int now=0;
for (int i=1;i<=len;i++)
{
int c=get(s[i]);
if (!t[now].son[c])
{
t[now].son[c]=++cnt;
t[cnt].fa=now;
t[cnt].len=t[now].len+1;
}
now=t[now].son[c];
}
num[d]=now;
}
void getfail()
{
for (int i=0;i<4;i++)
{
if (t[0].son[i])
{
t[i].fail=0;
q.push(t[0].son[i]);
}
}
while (!q.empty())
{
int now=q.front();
q.pop();
for (int i=0;i<4;i++)
{
if (t[now].son[i])
{
t[t[now].son[i]].fail=t[t[now].fail].son[i];
q.push(t[now].son[i]);
}
else
{
t[now].son[i]=t[t[now].fail].son[i];
}
}
}
}
void find(char *s)
{
int now=0;
for (int i=1;i<=n;i++)
{
int c=get(s[i]);
now=t[now].son[c];
for (int p=now;p;p=t[p].fail)
{
if (!t[p].vis) t[p].vis=1;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for (int i=1;i<=m;i++)
{
scanf("%s",Q+1);
ins(Q,i);
}
getfail();
find(s);
for (int i=1;i<=m;i++)
{
ans=0;
for (int p=num[i];p>0;p=t[p].fa)
{
if (t[p].vis)
{
ans=t[p].len;
break;
}
}
printf("%d\n",ans);
}
}