小t非常感謝大家幫忙解決了他的上一個問題。然而病毒侵襲持續中。在小t的不懈努力下,他發現了網路中的“萬惡之源”。這是一個龐大的病毒網站,他有着好多好多的病毒,但是這個網站包含的病毒很奇怪,這些病毒的特徵碼很短,而且只包含“英文大寫字符”。當然小t好想好想爲民除害,但是小t從來不打沒有準備的戰爭。知己知彼,百戰不殆,小t首先要做的是知道這個病毒網站特徵:包含多少不同的病毒,每種病毒出現了多少次。大家能再幫幫他嗎?
Input
第一行,一個整數N(1<=N<=1000),表示病毒特徵碼的個數。
接下來N行,每行表示一個病毒特徵碼,特徵碼字符串長度在1—50之間,並且只包含“英文大寫字符”。任意兩個病毒特徵碼,不會完全相同。
在這之後一行,表示“萬惡之源”網站源碼,源碼字符串長度在2000000之內。字符串中字符都是ASCII碼可見字符(不包括回車)。
Output
按以下格式每行一個,輸出每個病毒出現次數。未出現的病毒不需要輸出。
病毒特徵碼: 出現次數
冒號後有一個空格,按病毒特徵碼的輸入順序進行輸出。
Sample Input
3 AA BB CC ooxxCC%dAAAoen....END
Sample Output
AA: 2 CC: 1
Hint
Hit: 題目描述中沒有被提及的所有情況都應該進行考慮。比如兩個病毒特徵碼可能有相互包含或者有重疊的特徵碼段。 計數策略也可一定程度上從Sample中推測。 AC自動機模板題
只有一個文本串進行匹配,題目要求記錄文本串中出現多少個病毒特徵碼(注意不是多少種),那麼在遍歷後綴時
不用做標記,直接遍歷到底即可。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<iostream>
using namespace std;
const int maxn=5e4+7;
const int branch=27;
queue<int> mmp;
int val[1010];//存放病毒特徵碼出現的次數
class AcTireNode
{
public:
int next[branch];
int id,flag;//0爲初始值 num記錄該病毒編號 flag記錄該節點被flag編號的文本遍歷了
int fail;
void clear()
{
fail=0;
for(int i=0; i<branch; ++i)
next[i]=-1;
id=flag=0;
}
};
class AcTire
{
private:
AcTireNode *node;
int top;
public:
AcTire()
{
node=new AcTireNode[maxn];
node[0].clear();
top=0;
}
int hash_letter(char c)
{
if(c>='A'&&c<='Z')
return c-'A';
return 26;
}
void insert(char *s,int num)
{
int now=0;
while(*s)
{
int i=hash_letter(*s);
if(node[now].next[i]==-1)
{
node[now].next[i]=++top;
node[top].clear();
}
now=node[now].next[i];
++s;
}
node[now].id=num;
}
void Bulid_fail()//作用實現:找到每個節點所代表字符串的最大匹配
{
int now,to;
for(int i=0; i<branch; ++i)
{
if(node[0].next[i]!=-1)
mmp.push(node[0].next[i]);
}
while(!mmp.empty())
{
now=mmp.front();
mmp.pop();
for(int i=0; i<branch; ++i)
{
if(node[now].next[i]!=-1)
{
mmp.push(node[now].next[i]);
to=node[now].fail;
while(to>0&&node[to].next[i]==-1)
to=node[to].fail;
if(node[to].next[i]!=-1)
to=node[to].next[i];
node[node[now].next[i]].fail=to;
}
}
}
}
void Find_tx(char *tx)//計算文本串中所有病毒特徵碼出現的次數
{
int now=0;
int to;
while(*tx)
{
int i=hash_letter(*tx);
while(now>0&&node[now].next[i]==-1)
now=node[now].fail;
if(node[now].next[i]!=-1)//有匹配的後綴
{
now=node[now].next[i];
to=now;//開始找所有後綴匹配
while(to>0)//這個節點 曾經是否被這個字符串 遍歷過
{
if(node[to].id)
val[node[to].id]++;
to=node[to].fail;
}
}
++tx;
}
}
void init()
{
top=0;
node[0].clear();
}
~AcTire()
{
delete []node;
}
};
char tx[2001000],str[1010][60];
int main()
{
AcTire dch;
int n;
while(~scanf("%d",&n))
{
dch.init();
for(int i=1; i<=n; ++i)
{
scanf("%s",str[i]);
dch.insert(str[i],i);
}
dch.Bulid_fail();
scanf("%s",tx);
memset(val,0,sizeof(val));
dch.Find_tx(tx);
for(int i=1;i<=n;++i)
{
if(val[i])
printf("%s: %d\n",str[i],val[i]);
}
}
return 0;
}