剛剛學習了AC 自動機,先記錄一個數組寫法的模板。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Max 26
#define N 1000005
struct node
{
int next[Max];//每一個節點可以擴展到的字母
int fail;//每一個節點的失配指針
int count;
void init()//構造
{
memset(next,-1,sizeof(next));
fail=0;
count=0;
}
}s[N];
int num;//記錄節點編號
char str[55];//模板串,單詞
char ss[N];//母串
int q[N],tail,head;//隊列相關信息
void cas_init()//在整個程序前構造root
{
s[0].init();//初始化頭結點
num=1;//當前的節點數量爲1
}
void insert()
{
int len=strlen(str);
int p=0,i,j;
for(i=0;i<len;i++)
{
j=str[i]-'a';//求出字母在next中的編號
if(s[p].next[j]==-1)//如果爲空則構造新的,否則順着上次的開始往下構造
{
s[num].init();//初始化當前節點
s[p].next[j]=num++;//連向當前節點,並是num++來擴充節點
}
p=s[p].next[j];//向下遍歷
}
s[p].count++;//增加離根節點這條路徑
}
void build_ac()
{
tail=head=0;//初始化隊列
int temp,p;
for(int i=0;i<Max;i++)
{
if(s[0].next[i]!=-1)
{
q[tail++]=s[0].next[i];//和根節點相連的都入隊
}
}
while(tail!=head)
{
p=q[head++];//記錄隊首節點
for(int i=0;i<Max;i++)//遍歷首節點的next
{
if(s[p].next[i]!=-1)//如果節點next不爲空
{
q[tail++]=s[p].next[i];//將兒子節點入隊列
temp=s[p].fail;//記錄節點的失配指針指向
while(temp>0&&s[temp].next[i]==-1)//當失配指針不爲root時一直循環找到一的兒子節點不爲空或到了root
temp=s[temp].fail;
if(s[temp].next[i]!=-1)//如果當前節點有兒子的話記錄下來備用
temp=s[temp].next[i];
s[s[p].next[i]].fail=temp;//是當前節點的失配指針指向剛纔記錄的節點完成失配指針的構造
}
}
}
}
void query()
{
int len=strlen(ss);
int p=0;
int temp=0;
int ans=0;
for(int i=0;i<len;i++)
{
int id=ss[i]-'a';
while(p>0&&s[p].next[id]==-1)//當前p指針不是root和找不到兒子時,一直找下去(及kmp的while)
p=s[p].fail;//一直尋找失配指針
if(s[p].next[id]!=-1)//找到適合的失配指針
{
p=s[p].next[id];//指向這個兒子節點,更新p的值進行下次匹配
temp=p;
while(temp>0&&s[temp].count!=-1)//temp > 0表示還沒到root,count != -1表示指針前還有單詞
{
ans+=s[temp].count;//加上有的單詞個數
s[temp].count=-1;//不重複計算
temp=s[temp].fail;//一直尋找失配指針
}
}
}
printf("%d\n",ans);
}
int main()
{
int n;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
cas_init();
for(int i=1;i<=n;i++)
{
scanf("%s",str);
insert();
}
build_ac();
scanf("%s",ss);
query();
}
return 0;
}