题意:
给出一个匹配串和n个单词;
求每个单词在匹配串中出现的的最大前缀长度;
匹配串长度<=10^7,n<=10^5,单词长度<=100;
题解:
当年啥也不会天真的一发KMP骗掉了50分,然后看题解说是自动机感觉好神啊;
现在回来复习自动机就把这道题切了试试;
基本的建立自动机什么的不说了;
主要就是答案的处理上我是在trie树上记录一个is的数组;
然后每个和匹配串匹配到了的结点全都标记上;
最后和建树时一样扫一遍自动机,每个单词标记的最大前缀长度就是答案;(当然这里要把fail指针的一连串的后缀相同的结点标记)
建树和找答案的复杂度是O(100n),自动机匹配大概是O(10^7)左右咯;
有个小优化,找后缀时发现已经标记的就可以退出了,因为后面一定也被标记完成了;
大概可以省500ms;
还有一点。。。因为匹配串很长字符集很少单词很短;
所以大随机数据可以视为单词全在匹配串中从而可能骗掉4个点233333(orz gaoj,思路太神);
代码:
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100001
#define M 10000001
using namespace std;
queue<int>q;
int fail[M], next[M][4], root, tot = 1;
char str[M], a[N][101];
bool is[M];
int f(char a)
{
switch (a)
{
case 'E': return 0;
case 'S': return 1;
case 'W': return 2;
case 'N': return 3;
}
}
void insert(char *s)
{
int p=root,index;
while (*s != '\0')
{
index = f(*s);
if (next[p][index] == 0)
next[p][index] = ++tot;
p=next[p][index];
s++;
}
}
void Build()
{
int p, temp, i;
q.push(root);
while (!q.empty())
{
p = q.front(), q.pop();
for (i = 0; i < 4; i++)
{
if (next[p][i])
{
temp = fail[p];
while (temp)
{
if (next[temp][i])
{
fail[next[p][i]] = next[temp][i];
break;
}
temp = fail[temp];
}
if (!temp) fail[next[p][i]] = root;
q.push(next[p][i]);
}
}
}
}
void query(char *s)
{
int index, p, temp;
p = root;
while (*s != '\0')
{
index = f(*s);
while (next[p][index] == 0 && p)
p = fail[p];
p = p ? next[p][index] : root;
temp = p;
while (temp&&is[temp] == 0)
{
is[temp] = 1;
temp = fail[temp];
}
s++;
}
}
void slove(char *s)
{
int p = root, index, ret = 0;
while (*s != '\0')
{
index = f(*s);
if (is[next[p][index]])
ret++;
else
break;
p = next[p][index];
s++;
}
printf("%d\n", ret);
}
int main()
{
int n, m, i, j, k, len;
scanf("%d%d", &len, &n);
scanf("%s", str);
for (i = root = 1; i <= n; i++)
{
scanf("%s", a[i]);
insert(a[i]);
}
Build();
query(str);
for (i = 1; i <= n; i++)
{
slove(a[i]);
}
return 0;
}