题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5384
题面:
1 5 6 orz sto kirigiri danganronpa ooooo o kyouko dangan ronpa ooooo ooooo
1 1 0 3 7
解题:
比赛的时候,看到那么多人过了,也想着套套AC自动机,但不会就是不会呀。没有基础,赛后搜题解发现有人用字典树过了,就学习一下。虽然感觉复杂度上可能会超,但学习一下字典树也是不错的嘛!字典树的查询和构建是唯一的难点。因为刚接触还是不是很会,就参考了这篇博客的做法。
其实就是先将所有的B构建成一颗字典树,然后枚举每个A,从A的不同起始位置开始去匹配B,查询函数可以这样理解。当用A的一部分移动到B的字典树的某一节点时,说明当前往上的节点都已经匹配成功了,那么也就是A中包含由B的根节点到当前节点形成的路径,那么加上该节点对应的数量即可,一旦匹配失败,那么再往下也不可能了,直接返回此时的数量就好了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct Trie
{
//存储的节点
int ch[100010][26];
//val存储的是节点权值,sz是节点数量
int val[100010],sz;
//初始化
void init()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
//插入一条新的单词记录
void insert(char *s)
{
int u=0,len=strlen(s);
for(int i=0;i<len;i++)
{
int c=(s[i]-'a');
//如果该节点还不存在,创建该节点
if(!ch[u][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
//为该节点分配编号
ch[u][c]=sz++;
}
//向下移
u=ch[u][c];
}
val[u]++;
}
//查询该记录
int query(char *s)
{
int len=strlen(s),u=0,c,res=0;
for(int i=0;i<len;i++)
{
c=s[i]-'a';
if(ch[u][c])
{
//其实因为a的查询是以a的起始位置不断后移的
//这是从a往后不同长度匹配b
u=ch[u][c];
res+=val[u];
}
else
return res;
}
return res;
}
};
Trie T;
char a[100005][10010];
char ss[10010];
int main()
{
int n,m,t,len,ans;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
//初始化
T.init();
//先将a读入并存下来
for(int i=0;i<n;i++)
{
getchar();
scanf("%s",a[i]);
}
//用b构建字典树
for(int i=0;i<m;i++)
{
getchar();
scanf("%s",ss);
T.insert(ss);
}
//以a的不同起始位置取查询结果
for(int i=0;i<n;i++)
{
len=strlen(a[i]);
ans=0;
for(int j=0;j<len;j++)
{
ans+=T.query(a[i]+j);
}
printf("%d\n",ans);
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/david_jett/article/details/47701371