题目链接:http://acm.hust.edu.cn/problem.php?id=1609
题意:给出n个短串和一个长串,求长串中包含了多少个短串?
思路:首先将长串变短,因为每个字母连续出现的次数最多是n个串中该字母最长的次数。然后将n个串建立自动机。
const int MAX=1000005;
int C;
char s[MAX],s1[MAX*150],str[2005][200];
struct Node
{
int next[26],pre,leaf,flag;
void init()
{
memset(next,-1,sizeof(next));
pre=-1;
leaf=-1;
flag=0;
}
};
Node a[600005];
int e,root,n;
int Q;
void insert(char str[],int id)
{
int p=root;
for(int i=0;str[i];i++)
{
if(a[p].next[str[i]-'a']==-1) a[p].next[str[i]-'a']=++e;
p=a[p].next[str[i]-'a'];
}
a[p].leaf=id;
}
void build_DFA()
{
int i,p,q,k;
queue<int> Q;
Q.push(1);
while(!Q.empty())
{
p=Q.front();
Q.pop();
for(i=0;i<26;i++) if(a[p].next[i]!=-1)
{
q=a[p].next[i];
k=a[p].pre;
while(k!=-1)
{
if(a[k].next[i]!=-1)
{
a[q].pre=a[k].next[i];
break;
}
else k=a[k].pre;
}
Q.push(q);
}
}
}
int ans[2005];
void find(char str[])
{
int p=root,i,pre,k;
for(i=0;str[i];i++)
{
k=str[i]-'a';
while(p>=1)
{
if(a[p].next[k]!=-1)
{
p=a[p].next[k];
pre=p;
while(pre>=1&&a[pre].flag==0)
{
a[pre].flag=1;
if(a[pre].leaf!=-1) ans[a[pre].leaf]=1;
pre=a[pre].pre;
}
break;
}
else p=a[p].pre;
}
if(p<1) p=1;
}
}
void init()
{
int i;
for(i=0;i<=600000;i++) a[i].init();
for(i=0;i<26;i++) a[0].next[i]=1;
a[1].pre=0;
}
void deal()
{
int L=0,i,j,k,t,len,p[26]={0};
for(i=1;i<=n;i++)
{
len=strlen(str[i]);
for(j=0;j<len;j++)
{
k=1;
for(t=j+1;t<len&&str[i][t]==str[i][j];t++)
{
k++;
}
p[str[i][j]-'a']=max(p[str[i][j]-'a'],k);
j=t-1;
}
}
len=strlen(s);
for(i=0;i<len;i++)
{
if(s[i]>='a'&&s[i]<='z') s1[L++]=s[i];
else
{
k=0;
for(j=i+1;s[j]>='0'&&s[j]<='9';j++)
{
k=k*10+s[j]-'0';
}
i=j+1;
for(j=1;j<=p[s[i-1]-'a']+1&&j<=k;j++) s1[L++]=s[i-1];
}
}
s1[L]=0;
}
int main()
{
for(scanf("%d",&C);C--;)
{
scanf("%d",&n);
int i;
init();
root=e=1;
for(i=1;i<=n;i++) scanf("%s",str[i]),insert(str[i],i);
scanf("%s",s);
deal();
build_DFA();
memset(ans,0,sizeof(ans));
find(s1);
int A=0;
for(i=1;i<=n;i++) if(ans[i]) A++;
if(A) printf("Yes");
else printf("No");
printf(" %d\n",A);
}
return 0;
}