Link:http://acm.hdu.edu.cn/showproblem.php?pid=3065
病毒侵袭持续中
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8620 Accepted Submission(s): 3019
Problem Description
小t非常感谢大家帮忙解决了他的上一个问题。然而病毒侵袭持续中。在小t的不懈努力下,他发现了网路中的“万恶之源”。这是一个庞大的病毒网站,他有着好多好多的病毒,但是这个网站包含的病毒很奇怪,这些病毒的特征码很短,而且只包含“英文大写字符”。当然小t好想好想为民除害,但是小t从来不打没有准备的战争。知己知彼,百战不殆,小t首先要做的是知道这个病毒网站特征:包含多少不同的病毒,每种病毒出现了多少次。大家能再帮帮他吗?
Input
第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。
在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。
在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。
Output
按以下格式每行一个,输出每个病毒出现次数。未出现的病毒不需要输出。
病毒特征码: 出现次数
冒号后有一个空格,按病毒特征码的输入顺序进行输出。
病毒特征码: 出现次数
冒号后有一个空格,按病毒特征码的输入顺序进行输出。
Sample Input
3 AA BB CC ooxxCC%dAAAoen....END
Sample Output
AA: 2 CC: 1HintHit: 题目描述中没有被提及的所有情况都应该进行考虑。比如两个病毒特征码可能有相互包含或者有重叠的特征码段。 计数策略也可一定程度上从Sample中推测。
Source
AC code:
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#define LL long long
#define MAXN 2000010
using namespace std;
char str[MAXN];//源串
char p[1111][55];//模式串(即病毒)
struct Trie
{
int next[50010][128];//有128个可显字符(若题目给的只有小写字母,128可能会暴),还有,注意第一个下标容量大小!!!
//防止MLE或RE(access_violation),下面开的数组大小类似上面的数组第一个下标容量大小,
//第一个下标表示字典树最大结点数,也就是允许的最大总的字符总数
int fail[50010];//失配指针
int end[50010];//记录数组,若要输出出现模式串的id,用end记录id
int count[50010];//计数器
int root;//根结点指针
int L;//总长度
int NewNode() //获取新结点并初始化
{
for(int i=0;i<128;i++)
{
next[L][i]=-1;
}
end[L]=-1;
count[L]=0;
return L++;
}
void init() //初始化
{
L=0;
root=NewNode();
}
void Insert(char *s,int id)
{
int len=strlen(s);
int j=root;
for(int i=0;i<len;i++)
{
if(next[j][s[i]]==-1)//不存在该结点
{
next[j][s[i]]=NewNode();
}
j=next[j][s[i]];
}
end[j]=id;//记录其id
count[j]++;
}
void build()
{
queue<int>q;
fail[root]=root;//根结点失配指针指向自己
//根结点的孩子入队,其失配指针指向自己
for(int i=0;i<128;i++)
{
if(next[root][i]==-1)//不存在该孩子
{
next[root][i]=root;//指向自己
}
else
{
fail[next[root][i]]=root;//失配指针指向自己
q.push(next[root][i]); //孩子入队
}
}
int j;
while(!q.empty())
{
j=q.front();
q.pop();
for(int i=0;i<128;i++)
{
if(next[j][i]==-1)//不存在该孩子,指向其父结点失配指针所指向的结点(该结点也有孩子i)
{
next[j][i]=next[fail[j]][i];
}
else
{
fail[next[j][i]]=next[fail[j]][i];
q.push(next[j][i]);
}
}
}
}
bool used[1010];//记录数组
int query(char *str,int n,int id)
{
int len=strlen(str);
int j=root;
int temp;
int num[1010];num[i]表示病毒i的出现次数
memset(num,0,sizeof(num));//每个病毒出现次数
int ans1=0;//how many keywords will be match,即求目标串中出现了几个模式串(同一个模式串允许在源串的不同位置重复出现,出现一次,次数累加一次)
int ans2=0;//出现的模式串个数(同一个模式串不允许在源串的不同位置重复出现,即使出现多次,次数只算一次)
memset(used,false,sizeof(used));
for(int i=0;i<len;i++)
{
j=next[j][str[i]];
temp=j;
while(temp!=root)
{
ans1+=count[temp];
count[temp]=0;
if(end[temp]!=-1) //该单词或字符在Trie中出现了
{
used[end[temp]]=true;//记录
ans2++;
num[end[temp]]++;
}
temp=fail[temp];//继续找后缀串
}
}
if(ans2>0)//按字典序输出已出现过的模式串
{
// printf("web %d:",id);
for(int i=1;i<=n;i++)
{
if(used[i])
printf("%s: %d\n",p[i],num[i]);
}
}
//return ans1;//返回目标串中出现的模式串个数(出现多次,次数累加算)
return ans2;//返回目标串中已出现过的模式串个数(出现多次,次数只算一次)
}
};
Trie ac;
int main()
{
int n,m,tot,t;
while(scanf("%d",&n)!=EOF)
{
ac.init();
for(int i=1;i<=n;i++)
{
scanf("%s",p[i]);
ac.Insert(p[i],i);
}
ac.build();
//tot=0;
//scanf("%d",&m);
//for(int i=1;i<=m;i++)
//{
scanf("%s",str);
//if(ac.query(str,n,i)>0)
// tot++;
ac.query(str,n,1);
//}
//printf("total: %d\n",tot);
}
return 0;
}