题意:求一个文章(长度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. CLR 公共语言运行库

    1..支持多语言..只是语言是面向CLR的..均可以在此基础上运行. 2..程序集加载..程序打包之后的Dll文件由CLR(公共语言运行库)来编译并加载到可以执行状态..由CLR(公共语言运行库)加载 ...

  2. Centos6.5搭建java开发环境

    一.安装jdk 1.查看Linux自带的JDK是否已安装 java –version 如果出现openjdk,最好还是先卸载掉openjdk,在安装sun公司的jdk. 2.查看jdk信息 rpm - ...

  3. LVS简介

    LVS 编辑 LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一 ...

  4. JSF页面中使用js函数回调后台bean方法并获取返回值的方法

    由于primefaces在国内使用的并不是太多,因此,国内对jsf做系统.详细的介绍的资料很少,即使有一些资料,也仅仅是对国外资料的简单翻译或者是仅仅讲表面现象(皮毛而已),它们的语句甚至还是错误的, ...

  5. php开发中怎么获取服务端MAC地址?

    MAC(Media Access Control或者Medium Access Control)地址,意译为媒体访问控制,或称为物理地址.硬件地址,用来定义网络设备的位置.在php中如何获取MAC(M ...

  6. 开始使用pycharm了

    我将python的主力开发工具从eclipse+pydev切换到pycharm社区版了. 选择pycharm 的原因:1. pycharm可以实时按照pep8的规范检查code style和namin ...

  7. golang的json操作

    package main import ( "encoding/json" "fmt" "os" ) type ConfigStruct s ...

  8. Windows Phone自带的语音识别

    WindowsPhone下语音操作包括: 1.程序内部的语音识别,用户可以通过语音识别进行输入或完成相关任务 2.控制程序的语音命令,控制程序启动.打开,并可对页面跳转等进行操作 这篇文章将构建一个简 ...

  9. R语言练习(一)

    b = seq(from=0, to=1, by=0.001) #一次方 l1 = function(b){ b^1 } y1 = l1(b) #二次方 l2 = function(b){ b^2 } ...

  10. java系列-使用maven创建web项目(二)

    推荐2个maven找jar包配置的网站,只需要搜索关键字即可找到需要的Jar包,非常方便,比如:MySQL就可以找到mysql-connect-Java.jar. http://search.mave ...