时间限制:
10000ms
单点时限:
1000ms
内存限制:
256MB
描写叙述
我们有一个字符串集合S,当中有N个两两不同的字符串。
还有M个询问,每一个询问给出一个字符串w。求有多少S中的字符串能够由w加入恰好一个字母得到。
字母能够加入在包含开头结尾在内的任何位置,比方在"abc"中加入"x",就可能得到"xabc", "axbc", "abxc", "abcx".这4种串。
输入
第一行两个数N和M,表示集合S中字符串的数量和询问的数量。
接下来N行,当中第i行给出S中第i个字符串。
接下来M行,当中第i行给出第i个询问串。
全部字符串仅仅由小写字母构成。
数据范围:
N,M<=10000。
S中字符串长度和<=100000。
全部询问中字符串长度和<=100000。
输出
对每一个询问输出一个数表示答案。
3 3 tourist petr rng toosimple rg ptr
例子输出
0
1
1
解题思路:
用字典树做的。将第1个字符串集建树,对第2个字符串。去树上匹配。要在树上跳过1个节点的才是满足条件的,于是就递归枚举跳过的节点即可了。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>
#include <map>
#include <set>
using namespace std;
const int maxn=100000+100;
char s[maxn];
const int Max=27;
struct node
{
int sign;
int next[Max];
} a[maxn];
set<int> se;
int cur=0;
void insert(char *s)
{
int len,ans;
int p=0;
len=strlen(s);
for(int i=0; i<len; i++)
{
ans=s[i]-'a';
if(a[p].next[ans]!=0)
{
p=a[p].next[ans];
}
else
{
a[p].next[ans]=++cur;
a[cur].sign=0;
p=a[p].next[ans];
}
}
a[p].sign++;
}
//int ans;
int len;
void find(int sign,int p,int x)
{
if(x==len&&sign&&a[p].sign)
{
// ans+=a[p].sign;
se.insert(p);
//cout<<ans<<endl;
return;
}
if(sign==0&&x<=len)
{
for(int i=0; i<26; i++)
{
if(a[p].next[i]>0)
{
int tp=a[p].next[i];
find(1,tp,x);
}
}
}
if(x<len)
{
int ne=s[x]-'a';
if(a[p].next[ne]>0)
{
int tp=a[p].next[ne];
find(sign,tp,x+1);
}
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=0; i<maxn; i++)
{
cur=0;
a[i].sign=0;
memset(a[i].next,0,sizeof(a[i].next));
}
for(int i=0; i<n; i++)
{
scanf("%s",s);
insert(s);
}
for(int i=0; i<m; i++)
{
se.clear();
scanf("%s",s);
len=strlen(s);
//ans=0;
find(0,0,0);
printf("%d\n",se.size());
}
}
return 0;
}