A string is a finite sequence of symbols that are chosen from an alphabet. In this problem you are given a string Tand n queries each with a string Pi, where the strings contain lower case English alphabets only. You have to find the number of times Pi occurs as a substring of T.
Input
Input starts with an integer T (≤ 10), denoting the number of test cases.
Each case starts with a line containing an integer n (1 ≤ n ≤ 500). The next line contains the string T (1 ≤ |T| ≤ 106). Each of the next n lines contains a string Pi (1 ≤ |Pi| ≤ 500).
Output
For each case, print the case number in a single line first. Then for each string Pi, report the number of times it occurs as a substring of T in a single line.
Sample Input |
Output for Sample Input |
2 5 ababacbabc aba ba ac a abc 3 lightoj oj light lit |
Case 1: 2 3 1 4 1 Case 2: 1 1 0 |
剛開始沒看題目,用kmp做,超時,後來看了一下主題,知道要用AC算法。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <list>
using namespace std;
char t[1000002];
int** gt;
int* fail;
int* output;
int map[500];
char query[500][501];
int stateNum,maxStateNum;
int queryNum;
void generateGoto();
void generateFail();
void parse();
int main()
{
int caseNum;
maxStateNum=250;
fail=NULL;
output=(int*)malloc(sizeof(int)*maxStateNum);
gt=(int**)malloc(sizeof(int*)*maxStateNum);
for(int i=0;i<maxStateNum;++i)
gt[i]=(int*)malloc(sizeof(int)*26);
for(int i=0;i<maxStateNum;++i)
{
for(int j=0;j<26;++j)
gt[i][j]=-1;
fail[i]=-1;
output[i]=-1;
}
scanf("%d",&caseNum);
for(int i=0;i<caseNum;++i)
{
scanf("%d",&queryNum);
memset(t,0,1000002);
scanf("%s",t);
for(int j=0;j<queryNum;++j)
{
memset(query[j],0,501);
scanf("%s",query[j]);
}
stateNum=0;
generateGoto();
if(fail!=NULL)
free(fail);
fail=(int*)malloc(sizeof(int)*maxStateNum);
for(int x=0;x<maxStateNum;++x)
fail[x]=-1;
generateFail();
parse();
printf("Case %d:\n",i+1);
for(int p=0;p<queryNum;++p)
printf("%d\n",output[map[p]]);
for(int p=0;p<=stateNum;++p)
{
for(int q=0;q<26;++q)
gt[p][q]=-1;
fail[p]=output[p]=-1;
}
}
return 0;
}
void generateGoto()
{
for(int i=0;i<queryNum;++i)
{
int pos_gt=0;
for(int j=0;query[i][j]!='\0';++j)
{
if(gt[pos_gt][query[i][j]-'a']==-1)
{
gt[pos_gt][query[i][j]-'a']=++stateNum;
pos_gt=stateNum;
}
else
pos_gt=gt[pos_gt][query[i][j]-'a'];
if(stateNum==maxStateNum)
{
maxStateNum*=2;
int** tmp=(int**)malloc(sizeof(int*)*maxStateNum);
for(int x=0;x<maxStateNum;++x)
tmp[x]=(int*)malloc(sizeof(int)*26);
for(int x=0;x<stateNum;++x)
{
for(int y=0;y<26;++y)
tmp[x][y]=gt[x][y];
free(gt[x]);
}
free(gt);
for(int x=stateNum;x<maxStateNum;++x)
for(int y=0;y<26;++y)
tmp[x][y]=-1;
gt=tmp;
int* ot=(int*)malloc(sizeof(int)*maxStateNum);
for(int i=0;i<stateNum;++i)
ot[i]=output[i];
for(int i=stateNum;i<maxStateNum;++i)
ot[i]=-1;
free(output);
output=ot;
}
}
output[pos_gt]=0;
map[i]=pos_gt;
}
for(int i=0;i<26;++i)
if(gt[0][i]==-1)
gt[0][i]=0;
}
產生goto表,每次有新的模式,從起始狀態開始,若存在相應的路徑,則變換到該路徑的狀態,若沒有,則創建新的狀態,並轉到該狀態。
void generateFail()
{
int maxSizeOfQueue=500;
list<int> queue;
fail[0]=0;
for(int i=0;i<26;++i)
if(gt[0][i]!=-1&>[0][i]!=0)
{
queue.push_back(gt[0][i]);
fail[gt[0][i]]=0;
}
while(!queue.empty())
{
int currentState=queue.front();
queue.pop_front();
for(int i=0;i<26;++i)
if(gt[currentState][i]!=-1&>[currentState][i]!=0)
{
int st=fail[currentState];
while(gt[st][i]==-1)
st=fail[st];
fail[gt[currentState][i]]=gt[st][i];
queue.push_back(gt[currentState][i]);
}
}
}
從0狀態開始,0狀態不會失敗,1狀態失敗總是到0。然後層次遍歷,對於fail[r],查看他的前一個狀態p,p和r之間有‘a’邊,若fail[p]存在‘a’的邊,則fail[r]=goto(fail[p],a)。若沒有,則檢查fail[fail[p]],直到存在.0狀態不會失敗,確保迭代終止。
void parse()
{
int pos_gt=0;
for(int i=0;t[i]!='\0';++i)
{
while(gt[pos_gt][t[i]-'a']==-1)
pos_gt=fail[pos_gt];
pos_gt=gt[pos_gt][t[i]-'a'];
int tgt=pos_gt;
while(tgt!=0)
{
if(output[tgt]!=-1)
++output[tgt];
tgt=fail[tgt];
}
}
}
最後遍歷主串。