您的位置:首页 > 其它

bzoj 1212 [HNOI2004] L语言(不用AC自动机)

2015-07-12 16:01 465 查看
网上的题解大多树都要建一棵trie树,并在上面跑AC自动机,然而这里有一种同样需要trie树,但时间复杂度较低的方法。

首先,我们可以轻松列出状态转移方程 F[x]=∑| F[x-len(i)]&(is(i->x,s[i]);

这样的复杂度是O(m*lens*∑len[i]*n),可能会超时,再加上hash之类的就可以过了,但这显然不优美。

====================分割线====================

对于每个F(i),我们都是从之前的额某个F(j)转移过来的,它是true当且仅当(j+1->i)是一个单词,且f[j]是true,那么我们将每个单词反过来建一棵trie树,例如有单词abc,我们将cba插入trie树,从i开始i先匹配到每个单词的最后一位,然后再匹配到最后一位相同的倒数第二位,如此下去,当我们匹配到一个单词的开头时,并且此时的F[i-depth]为true的话,F(i)就为true了,因为每个字符在trie树上的路径唯一,且trie树的深度不超过单词的最长长度(10),所以它的复杂度还是非常可看的,复杂度为O(m*lens*dep(trie))=O(m*lens*max(strlen(word))),20*1M*10,轻松过。

这题不难,但是如果反过来想,可以避免很多高端算法,从后往前的思想确实不错。贴一个代码,有些冗长。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int ch[10005][20];
int cnt;
int n,m;
char s[2000005];
int col[2000005];
bool dp[1000005];
void insert()
{
int now=0;
for(int i=strlen(s)-1;i>=0;i--)
{
int c=s[i]-'0';
if(!ch[now][c])
{
cnt++;
ch[now][c]=cnt;
}
now=ch[now][c];
}
col[now]=true;
}
bool cal(int x)
{
int deep=0;
int now=0;
for(int i=x;i>=1;i--)
{
int c=s[i]-'0';
if(ch[now][c]==0)return false;
now=ch[now][c];
deep++;
if(dp[x-deep] && col[now])return true;
}
return true;
}
void solve()
{
int ll=strlen(s+1);
for(int i=1;i<=ll;i++)
{
dp[i]=cal(i);
}
for(int i=ll;i>=0;i--)
{
if(dp[i])
{
printf("%d\n",i);
return;
}
}
return ;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
insert();
}
for(int i=1;i<=m;i++)
{
memset(dp,0,sizeof(dp));
dp[0]=true;
scanf("%s",s+1);
solve();
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: