这道题其实就是一道裸的AC自动机板子题,只需要在建tire图的时候稍微处理一下,因为它求的是子串的最长前缀,那么我们可以将子串再划分为许多长度不同的前缀就行了,然后再记录一下划分出来的前缀分别属于哪一个子串。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N = 1e7 + 5;

char str[N], s[N];
int tr[N][4], cnt[N], net[N], idx, mp[200], ans[N];
bool vis[N];
queue<int> q;
vector<int> v, bel[N];

void insert(int x)
{
int p = 0;
for (int i = 0; str[i]; i++)
{
int t = mp[(int)str[i]];
if (!tr[p][t])
tr[p][t] = ++idx;
p = tr[p][t];
cnt[p] = i + 1;//记录每一个前缀的长度
bel[p].push_back(x);//记录当前节点可以更新哪些子串
}
}

void build()
{
for (int i = 0; i < 4; i++)
if (tr[0][i])
q.push(tr[0][i]);
while (!q.empty())
{
int t = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int p = tr[t][i];
if (!p)
tr[t][i] = tr[net[t]][i];
else
{
net[p] = tr[net[t]][i];
q.push(p);
}
}
}
}//AC自动机

int main()
{
mp['N'] = 0, mp['W'] = 1, mp['S'] = 2, mp['E'] = 3;
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", &s);
for (int i = 1; i <= m; i++)
{
scanf("%s", &str);
insert(i);
}
build();
for (int i = 0, j = 0; s[i]; i++)
{
int t = mp[(int)s[i]];
j = tr[j][t];
int p = j;
while (p)
{
if (vis[p])
break;//一个优化,即先把所有的找出来,然后在一起更新
vis[p] = true;
v.push_back(p);
p = net[p];
}
}
for (int i = 0; i < v.size(); i++)
{
int p = v[i];
for (int j = 0; j < bel[p].size(); j++)
ans[bel[p][j]] = max(ans[bel[p][j]], cnt[p]);//更新答案
}
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
return 0;
}