题意:
给出n个单词,求在这n个单词组成的文章中每个单词出现了多少次;
n<=200,n个字符串总长度大概是500000 (数据范围显然有误);
题解:
一开始考虑就是先建个自动机,然后对每个串匹配一次;
经过的所有节点 和后缀的cnt全部+1;
然后查询每个单词的危险结点的cnt值就是答案;
复杂度O(n*len+k*n*len)基本也是线性的,似乎可过;
结果我T了;
然后查了一下题解,有种更好的思路,不需要用串去在自动机上匹配;
就是利用fail指针反向来构建一颗fail树;
根据fail指针的特性,倘若从树上某点x向根走,经过的每个点所代表的字符串都是x的后缀;首先因为除了根结点每个结点都有fail指针所以是n-1条边;
而显然所以fail指针最后都会回到root,即反向后的fail树中root可以到达所有点;
n点n-1边并且可以从一个点到达所有,这就是一颗有向树了;
而一个字符串在另一个中的条件可以说成是,这个字符串是另一个字符串的某前缀的后缀;
那么可以想到从一个串的所有前缀向上拓展,每个能经过的点的cnt都增加;
这似乎是个动态规划的样子,具体实现就是:
建立自动机时将路径每个点的cnt都+1 (作为这个字符串的前缀);
对每个fail建立一个反向边;
根据反向边做树形dp,姿势和求子树权值和是一样的;
cnt[x]=∑cnt[ son[x] ] +cnt[x],得到的cnt就是答案;
然后!我交上去居然还是T!其实这步也可以记录宽搜时的BFS序,反过来枚举就是满足的;
然后!。。我发现我模板少敲一句话;
好了,解决之后上面的两种方法都是可以A的,只是时间不同 (分明就是卡时啊);
1036514 | 140142 | 3172 | Accepted | 158720 kb | 10940 ms | 1456 B | 2015-07-07 21:34:16 |
1036511 | 140142 | 3172 | Accepted | 160680 kb | 252 ms | 1235 B | 2015-07-07 21:33:15 |
代码:
正解:
#include<queue>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 510000
using namespace std;
char a[201][N];
int next[N][27],fail[N],cnt[N],ans[201],q[N];
int st,en,root=1,tot=1;
int insert(char *s)
{
int index,p;
p=root;
while(*s!='\0')
{
index=*s-'a';
if(!next[p][index])
next[p][index]=++tot;
p=next[p][index];
*s++;
cnt[p]++;
}
return p;
}
void Build()
{
int i,p,temp;
q[st=en=1]=root;
while(st<=en)
{
p=q[st++];
for(i=0;i<26;i++)
{
if(next[p][i])
{
temp=fail[p];
while(temp)
{
if(next[temp][i])
{
fail[next[p][i]]=next[temp][i];
break;
}
temp=fail[temp];
}
if(!temp) fail[next[p][i]]=root;
q[++en]=next[p][i];
}
}
}
}
void slove()
{
while(en)
cnt[fail[q[en]]]+=cnt[q[en--]];
}
int main()
{
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",a[i]);
ans[i]=insert(a[i]);
}
Build();
slove();
for(i=1;i<=n;i++)
printf("%d\n",cnt[ans[i]]);
return 0;
}
歪解:
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 510000
using namespace std;
queue<int>q;
char a[201][N];
int next[N][27],fail[N],cnt[N],ans[201],root=1,tot=1;
int insert(char *s)
{
int index,p;
p=root;
while(*s!='\0')
{
index=*s-'a';
if(!next[p][index])
next[p][index]=++tot;
p=next[p][index];
*s++;
}
return p;
}
void Build()
{
int i,p,temp;
q.push(root);
while(!q.empty())
{
p=q.front(),q.pop();
for(i=0;i<26;i++)
{
if(next[p][i])
{
temp=fail[p];
while(temp)
{
if(next[temp][i])
{
fail[next[p][i]]=next[temp][i];
break;
}
temp=fail[temp];
}
if(!temp) fail[next[p][i]]=root;
q.push(next[p][i]);
}
}
}
}
void query(char *s)
{
int index,p,temp;
p=root;
while(*s!='\0')
{
index=*s-'a';
while(!next[p][index]&&!p)
p=fail[p];
p=p?next[p][index]:root;
temp=p;
while(temp)
{
cnt[temp]++;
temp=fail[temp];
}
s++;
}
}
int main()
{
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",a[i]);
ans[i]=insert(a[i]);
}
Build();
for(i=1;i<=n;i++)
query(a[i]);
for(i=1;i<=n;i++)
printf("%d\n",cnt[ans[i]]);
return 0;
}