vijos-1951 玄武密码

来源:互联网 发布:阿里云创业创新基地 编辑:程序博客网 时间:2024/05/16 01:06

题意:

给出一个匹配串和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 10000001using 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++;elsebreak;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;}


0 0
原创粉丝点击