题意:求一个文章(长度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的更多相关文章

  1. poj4052 Hrinity

    pdf题面:传送门 题目大意:给定一些单词和一个句子,问有多少个单词在句子中出现过,如果一个但单词包含另一个单词,并且两个单词都出现过,那么只算最外层的单词(包含另一个单词的单词). 分析:这道题如果 ...

随机推荐

  1. tmux常用命令

    tmux命令可以启动一个tmux服务,tmux服务包含多个session,session包含多个window,window包含多个pane. 常用命令tmux ls #显示已有tmux列表(C-b s ...

  2. Java多线程编程核心技术---单例模式与多线程

    立即加载/饿汉模式 立即加载就是使用类的时候已经将对象创建完毕. public class MyObject { //立即加载方式==饿汉模式 private static MyObject myOb ...

  3. Yii2-Redis使用小记 - Cache(转)

    前些天简单学习了下 Redis,现在准备在项目上使用它了.我们目前用的是 Yii2 框架,在官网搜索了下 Redis,就发现了yii2-redis这扩展. 安装后使用超简单,打开 common/con ...

  4. C# Emit动态代理生成一个实体对象

    /// <summary> /// 使用Emit动态代理收集实体信息 /// </summary> /// <typeparam name="T"&g ...

  5. pch

    #define kWeakSelf(weakSelf) __weak __typeof(self)weakSelf = self; #ifndef __OPTIMIZE__#define NSLog( ...

  6. [转]Ubuntu 16.04建议安装

    Ubuntu 16.04发布了,带来了很多新特性,同样也依然带着很多不习惯的东西,所以装完系统后还要进行一系列的优化. 1.删除libreoffice libreoffice虽然是开源的,但是Java ...

  7. visio2007使用记录

    安装时, 需要有选择性的安装, 不是所有的组件 都需要安装, 如office工具, office共享功能, visio中的.net可编程支持就用不着安装.... visio中的cad(加载项), 可以 ...

  8. array_fill 用给定的值填充数组

    转自:http://www.phpstudy.net/php/165.html PHP array_fill 用给定的值填充数组 array_fill (PHP 4 >= 4.2.0, PHP ...

  9. Linux下的特殊权限SetUID

    1.SetUID的功能 只有可以执行的二进制程序才能设置SUID权限 命令执行者要对改程序拥有x执行权限 命令执行者在执行改程序的时候获得该程序文件属主的身份(在执行程序的过程中灵魂附体为文件的属性) ...

  10. QT文件读写

    /* //文件读取 QFile f("c:\\t.txt"); if(!f.open(QIODevice::WriteOnly | QIODevice::Text)) { qDeb ...