poj4052
题意:求一个文章(长度5.1e6)里面出现了多少个指定的模式串。重复出现只记一次。而且如果两个模式串都出现的情况下,一个是另一个的子串,则该子串不算出现过。
分析:AC自动机。
由于子串不算所以加一些特殊处理:
1.在文章匹配过程中,如果出现了一个模式串我们不是把匹配数量+1,而是记录那个出现过vis[id] = true;,当然trie树种也是记录了模式串的id。
2.在匹配结束后,我们遍历所有出现过的模式串,在Trie树种找到其所有出现过的子串并将其标为未出现过vis[id] = false;
要如何查找子串呢?
只需要记录每个出现过的串所对应的Trie树中的节点位置,由该节点向上走到root。其间走过的每个节点都沿着fail指针走到root一次。这样二重循环遍历到的所有节点就对应了Trie中所有该模式串的子串。
因为在AC自动机中父节点指针就是找前缀,fail指针就是找后缀。
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cctype>
using namespace std; #define D(x)
const int MAX_LEN = (int)(5.1e6) + ;
const int MAX_N = ;
const int MAX_FINGER_LEN = ;
const int MAX_CHILD_NUM = ;
const int MAX_NODE_NUM = MAX_N * MAX_FINGER_LEN; int n;
char st[MAX_LEN];
char st2[MAX_LEN];
int vis[MAX_N];
bool check[MAX_NODE_NUM]; struct Trie
{
int next[MAX_NODE_NUM][MAX_CHILD_NUM];
int fail[MAX_NODE_NUM];
int count[MAX_NODE_NUM];
int father[MAX_NODE_NUM];
int node_cnt;
int root; void init()
{
node_cnt = ;
root = newnode();
} int newnode()
{
for (int i = ; i < ; i++)
next[node_cnt][i] = -;
count[node_cnt++] = ;
return node_cnt - ;
} int get_id(char a)
{
return a - 'A';
} void insert(char buf[], int index)
{
int len = strlen(buf);
int now = root;
for (int i = ; i < len; i++)
{
int id = get_id(buf[i]);
if (next[now][id] == -)
{
next[now][id] = newnode();
father[next[now][id]] = now;
}
now = next[now][id];
}
count[now] = index;
} void build()
{
queue<int>Q;
fail[root] = root;
father[root] = root;
for (int i = ; i < ; i++)
if (next[root][i] == -)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while (!Q.empty())
{
int now = Q.front();
Q.pop();
for (int i = ; i < ; i++)
if (next[now][i] == -)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
} int query(char buf[])
{
int now = root;
int res = ;
for (int i = ; buf[i]; i++)
{
now = next[now][get_id(buf[i])];
int temp = now;
while (temp != root && !check[temp])
{
if (count[temp] != )
vis[count[temp]] = temp;
check[temp] = true;
temp = fail[temp];
}
}
return res;
} void debug()
{
for(int i = ;i < node_cnt;i++)
{
printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],count[i]);
for(int j = ;j < ;j++)
printf("%2d",next[i][j]);
printf("]\n");
}
} void cal()
{
for (int i = ; i <= n; i++)
{
if (vis[i] == )
{
continue;
}
int temp = vis[i];
while (temp != root)
{
int temp2 = temp;
while (temp2 != root && !check[temp2])
{
if (count[temp2] != && count[temp2] != i)
{
vis[count[temp2]] = ;
check[temp2] = true;
}
temp2 = fail[temp2];
}
temp = father[temp];
}
}
}
}; Trie ac; void transform(char st[], char st2[])
{
int len = ;
for (int i = ; st[i]; i++)
{
if (isupper(st[i]))
{
st2[len++] = st[i];
continue;
}
i++;
int temp = ;
while (isdigit(st[i]))
{
temp *= ;
temp += st[i] - '';
i++;
}
for (int j = ; j < temp; j++)
{
st2[len + j] = st[i];
}
len += temp;
i++;
}
st2[len] = ;
} void input()
{
scanf("%d", &n);
for (int i = ; i <= n; i++)
{
scanf("%s", st);
transform(st, st2);
ac.insert(st2, i);
}
} int work()
{
memset(vis, , sizeof(vis));
memset(check, , sizeof(check));
ac.query(st2);
memset(check, , sizeof(check));
ac.cal();
int ret = ;
for (int i = ; i <= n; i++)
{
if (vis[i])
{
D(printf("#%d\n", vis[i]));
ret++;
}
}
return ret;
} int main()
{
int t;
scanf("%d", &t);
while (t--)
{
ac.init();
input();
ac.build();
scanf("%s", st);
transform(st, st2);
printf("%d\n", work());
}
return ;
}
poj4052的更多相关文章
- poj4052 Hrinity
pdf题面:传送门 题目大意:给定一些单词和一个句子,问有多少个单词在句子中出现过,如果一个但单词包含另一个单词,并且两个单词都出现过,那么只算最外层的单词(包含另一个单词的单词). 分析:这道题如果 ...
随机推荐
- 在Linux下如何使用GCC编译程序、简单生成 静态库及动态库
最近在编写的一个Apache kafka 的C/C++客户端,,在看他写的 example中,他的编译是用librdkafka++.a和librdkafka.a 静态库编译的,,,而我们这 ...
- PHP 5.5 新特性
文章转自:http://wulijun.github.io/2013/07/17/whats-new-in-php-5-5.html http://www.cnblogs.com/yjf512/p/3 ...
- vim 树形目录插件NERDTree安装及简单用法
转自: http://blog.csdn.net/love__coder/article/details/6659103 1,安装NERDTree插件 先下载,官网:http://www.vim.or ...
- Mysql CPU占用90%
今天网站打开卡,查了下发现Mysql CPU占用到90%! 1.通过putty工具连接mysql putty工具十分的强大,它可以让我们直接访问MySQL.当然,要实现这一步肯定要做点什么.MySQL ...
- android 读取SQLite android could not open the database in read/write mode错误
由于AndroidManifest.xml文件中uses-permission没有设置权限问题 <uses-permission android:name="android.permi ...
- ajax浅析---ScriptManager
使用ScriptManager控件 它用来处理页面上的所有组件以及页面局部更新,生成相关的客户端代理脚本以便能够在JavaScript中访问Web Service,所有需要支持ASP.NET AJAX ...
- Problem B Boxes in a Line
省赛B题....手写链表..其实很简单的.... 比赛时太急了,各种手残....没搞出来....要不然就有金了...注:对相邻的元素需要特判..... Problem B Boxes in a Li ...
- 部分LINUX系统由图形界面启动变更为命令行界面启动的方法
背景: 图形界面很绚丽,但是现在并不需要图形界面,只需要命令行即可,所以要将图形界面自启动给关闭. 正文: Centos: 更改文件/etc/inittab的其中一行 id:5 ...
- 第12天 android studio
1. http://jingyan.baidu.com/article/215817f7888dc21eda14230d.html Gradle DSL method not found:‘andro ...
- 常用开源镜像站整理android sdk manager
http://www.cocoachina.com/programmer/20151023/13852.html http://android-mirror.bugly.qq.com:8080/inc ...