题意:
给出一个匹配串和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; }
时间: 2024-10-01 09:05:28