trie樹+kmp鞠绰。四瘫。fail指針其實(shí)就是相當(dāng)于kmp那個(gè)未優(yōu)化的next數(shù)組捶索,考慮到fail是有方向的八孝,方向可以理解成當(dāng)前這個(gè)(到這個(gè)節(jié)點(diǎn)為止)的后綴是之前一個(gè)短串的后綴董朝,是比root到當(dāng)前這個(gè)短的,所以在統(tǒng)計(jì)的時(shí)候干跛,才能保證fail往前走就可以子姜。
統(tǒng)計(jì)時(shí)候,fail數(shù)組一直往前,一路相加(這里設(shè)置了last數(shù)組哥捕,就是fail數(shù)組中節(jié)點(diǎn)是模式串結(jié)尾的點(diǎn))
插入:
void Insert(int v){
int u = 0,len = strlen(ss[v]);
for(int i = 0 ; i < len ; i++){
int tmp = ss[v][i]-'A';
if(!ch[u][tmp]){
memset(ch[node],0,sizeof(ch[node]));
val[node] = 0;
ch[u][tmp] = node++;
}
u = ch[u][tmp];
}
val[u] = v;
}
fail:
fail指針指向的問題和KMP算法中構(gòu)造next數(shù)組的方式如出一轍牧抽。具體方法如下
1)將根結(jié)點(diǎn)的所有孩子結(jié)點(diǎn)的fail指向根結(jié)點(diǎn),然后將根結(jié)點(diǎn)的所有孩子結(jié)點(diǎn)依次入列遥赚。
2)若隊(duì)列不為空:
2.1)出列扬舒,我們將出列的結(jié)點(diǎn)記為curr, failTo表示curr的fail指向的結(jié)點(diǎn),即failTo = curr.fail
2.2) a.判斷curr.child[i] == failTo.child[i]是否成立凫佛,
成立:curr.child[i].fail = failTo.child[i]讲坎,
不成立:判斷 failTo == null是否成立
成立: curr.child[i].fail == root
不成立:執(zhí)行failTo = failTo.fail,繼續(xù)執(zhí)行2.2)
b.curr.child[i]入列愧薛,再次執(zhí)行再次執(zhí)行步驟2)
若隊(duì)列為空:結(jié)束
//ch【】【】:第二維ascii碼范圍伺通,開260.
//last數(shù)組逢唤,就是fail數(shù)組中節(jié)點(diǎn)是模式串結(jié)尾的點(diǎn))
void getfail(){
queue<int> q;
fail[0] = 0;
for(int i = 0 ; i < ascs ; i++){
if(ch[0][i]){
fail[ch[0][i]] = 0;
q.push(ch[0][i]);
last[ch[0][i]] = 0;
}
}
while(!q.empty()){
int tmp = q.front();
q.pop();
for(int i = 0 ; i < ascs ; i++){
int u = ch[tmp][i];
if(u != 0){
q.push(u);
int v = fail[tmp];
while(v && ch[v][i]== 0) v = fail[v];
fail[u] = ch[v][i];
last[u] = val[fail[u]]?fail[u]:last[fail[u]];
}
}
}
}
查詢:
void Find(){
int len = strlen(s),j = 0;
for(int i = 0 ; i < len ; i++){
// if(s[i] <'A' ||s[i] > 'Z') continue;
int tmp = s[i]-'A';
while(j && ch[j][tmp] == 0) j = fail[j];
j = ch[j][tmp];
if(val[j]) cal(j);
else if(last[j]) cal(last[j]);
}
}
Problem Description
小t非常感謝大家?guī)兔鉀Q了他的上一個(gè)問題。然而病毒侵襲持續(xù)中。在小t的不懈努力下航罗,他發(fā)現(xiàn)了網(wǎng)路中的“萬惡之源”靴寂。這是一個(gè)龐大的病毒網(wǎng)站塘揣,他有著好多好多的病毒辛掠,但是這個(gè)網(wǎng)站包含的病毒很奇怪,這些病毒的特征碼很短丰榴,而且只包含“英文大寫字符”货邓。當(dāng)然小t好想好想為民除害,但是小t從來不打沒有準(zhǔn)備的戰(zhàn)爭四濒。知己知彼换况,百戰(zhàn)不殆,小t首先要做的是知道這個(gè)病毒網(wǎng)站特征:包含多少不同的病毒盗蟆,每種病毒出現(xiàn)了多少次戈二。大家能再幫幫他嗎?
Input
第一行喳资,一個(gè)整數(shù)N(1<=N<=1000)觉吭,表示病毒特征碼的個(gè)數(shù)。
接下來N行仆邓,每行表示一個(gè)病毒特征碼鲜滩,特征碼字符串長度在1—50之間,并且只包含“英文大寫字符”节值。任意兩個(gè)病毒特征碼徙硅,不會(huì)完全相同。
在這之后一行搞疗,表示“萬惡之源”網(wǎng)站源碼嗓蘑,源碼字符串長度在2000000之內(nèi)。字符串中字符都是ASCII碼可見字符(不包括回車)。
Output
按以下格式每行一個(gè)桩皿,輸出每個(gè)病毒出現(xiàn)次數(shù)豌汇。未出現(xiàn)的病毒不需要輸出。
病毒特征碼: 出現(xiàn)次數(shù)
冒號(hào)后有一個(gè)空格泄隔,按病毒特征碼的輸入順序進(jìn)行輸出拒贱。
Sample Input
3
AA
BB
CC
ooxxCC%dAAAoen….END
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int n,node;
const int maxn = 50*1000+10;
const int ascs = 200;
const int mm = 2e6+10;
char s[mm],ss[1010][51];
int ch[maxn][ascs],fail[maxn],val[maxn],last[maxn],cnt[mm];
void Insert(int v){
int u = 0,len = strlen(ss[v]);
for(int i = 0 ; i < len ; i++){
int tmp = ss[v][i]-'A';
if(!ch[u][tmp]){
memset(ch[node],0,sizeof(ch[node]));
val[node] = 0;
ch[u][tmp] = node++;
}
u = ch[u][tmp];
}
val[u] = v;
}
void getfail(){
queue<int> q;
fail[0] = 0;
for(int i = 0 ; i < ascs ; i++){
if(ch[0][i]){
fail[ch[0][i]] = 0;
q.push(ch[0][i]);
last[ch[0][i]] = 0;
}
}
while(!q.empty()){
int tmp = q.front();
q.pop();
for(int i = 0 ; i < ascs ; i++){
int u = ch[tmp][i];
if(u != 0){
q.push(u);
int v = fail[tmp];
while(v && ch[v][i]== 0) v = fail[v];
fail[u] = ch[v][i];
last[u] = val[fail[u]]?fail[u]:last[fail[u]];
}
}
}
}
void cal(int j){
if(j){
cnt[val[j]]++;
cal(last[j]);
}
}
void Find(){
int len = strlen(s),j = 0;
for(int i = 0 ; i < len ; i++){
// if(s[i] <'A' ||s[i] > 'Z') continue;
int tmp = s[i]-'A';
while(j && ch[j][tmp] == 0) j = fail[j];
j = ch[j][tmp];
if(val[j]) cal(j);
else if(last[j]) cal(last[j]);
}
}
void init(){
node = 1;
memset(ch[0],0,sizeof(ch[0]));
memset(cnt, 0, sizeof(cnt[0])*(n+2));
for(int i = 1 ;i <= n ; i++){
scanf("%s",ss[i]);
//cout << ss[i]<<endl;
Insert(i);
}
getfail();
}
void sov(){
scanf("%s",s);
Find();
for(int i = 1; i <= n ; i ++){
if(cnt[i] == 0) continue;
printf("%s: %d\n",ss[i],cnt[i]);
}
}
int main(){
while(~scanf("%d",&n)){
init();
sov();
}
return 0;
}