hdu2222 ac自动机。。。。
2014-04-22 14:29
429 查看
http://acm.hdu.edu.cn/showproblem.php?pid=2222
题目链接在此~
说下题目大意 给出case数,多组数据,每组数据中有n个子串,给出目标串,求出共有多少个子串在目标串中出现过,输出数量。
一开始的思路是运用hash,枚举子串长度,计算hash值,相等则答案加一。。妥妥的tle。。。
附上代码求大牛优化。。。
(就算不tle也有可能wa。。。)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char word[11000][60];
char x[1100000];
typedef unsigned long long ll;
ll hash[1100000][60];
bool hashs[1100000][60]={false};
ll xp[1100000];
ll hash1[1100000][60];
int base=31;
int N;
int lens;
bool fail=false;
ll gethash(char * a,ll hashr[][60])
{
fail=false;
int len=strlen(a);
int i;
hashr[len][0]=0;
for(i=len-1;i>=0;i--)
{
if(a[i]<'a'&&a[i]>'z')
{
fail=true;
break;
}
hashr[i][0]=hashr[i+1][0]*base+a[i]-'a'+1;
}
return hashr[0][0];
}
int find_sub()
{
int i;
int ans=0;
for(i=0;i<N;i++)
{
ll temp=gethash(word[i],hash1);
if(fail)
return 0;
// cout<<temp<<endl;
int length=strlen(word[i]);
//cout<<lens<<endl;
int j;
for(j=0;j<=lens-length;j++)
{
if(!hashs[j][length])
{
//cout<<1<<endl;
hash[j][length]=hash[j][0]-hash[j+length][0]*xp[length];
hashs[j][length]=true;
}
//cout<<i<<" "<<j<<" "<<hash[j]-hash[j+length]*xp[length]<<endl;
if(temp==hash[j][length])
{ans++;break;}
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
xp[0]=1;
for(int i=1;i<1100000;i++)
{
xp[i]=xp[i-1]*base;
}
while(T--)
{
memset(hashs,0,sizeof(hashs));
scanf("%d",&N);
int i;
for(i=0;i<N;i++)
{
scanf("%s",word[i]);
}
scanf("%s",x);
lens=strlen(x);
gethash(x,hash);
if(fail)
return 0;
printf("%d\n",find_sub());
}
return 0;
}
咳咳。步入正题。
因为运用hash多次超时,然后发现这道题被作为了ac自动机的模板题。。
当看到这个名字的时候别提多激动了。。。有了这个还比什么acm啊。。。ac自动啊。。。
当我得知真相的那一刻。。。唉。
ac自动机有三步,构建trie树,bfs构建fail指针,最后进行目标串匹配。
个人任务构建fail指针最难没有之一。。毕竟kmp还运用不熟练呢。。。
参考大牛代码作出如下ac自动机。。。。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int kind=26;
struct node{
node * fail;
node * next[kind];
int count;
node()
{
memset(next,0,sizeof(next));
count=0;
fail=NULL;
}
}*q[510000];//队列
char word[60];//子串
char words[1100000];//目标串
int head,tail;
void insert(node* root,char *x)//trie树插入操作
{
int i=0,index;
node* p=root;
while(x[i])
{
index=x[i]-'a';
if(p->next[index]==NULL)
p->next[index]=new node();
p=p->next[index];
i++;
}
p->count++;
}
void build_fail(node* root)//构建指针
{
int i;
root->fail=NULL;
q[head++]=root;
while(head!=tail)//队列
{
node* temp=q[tail++];//出队
node*p=NULL;
for(i=0;i<26;i++)//遍历
{
if(temp->next[i]!=NULL)
{
if(temp==root)//root的子节点fail为root
{
temp->next[i]->fail=root;
}
else{
p=temp->fail;
while(p!=NULL)
{
if(p->next[i]!=NULL)
{
temp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL)
{
temp->next[i]->fail=root;
}
}
q[head++]=temp->next[i];//入队
}
}
}
}
int ac_auto(node* root)//匹配。。
{
int i=0,c=0,index;
node*p=root;
while(words[i])
{
index=words[i]-'a';
while(p->next[index]==NULL&&p!=root)//失配转向fail指针
p=p->fail;
p=p->next[index];
p=(p==NULL)?root:p;
node* temp=p;
while(temp!=root&&temp->count)//重复匹配过程
{
c+=temp->count;
temp->count=0;
temp=temp->fail;
}
i++;
}
return c;
}
int main()
{
int n,cases;
scanf("%d",&cases);
while(cases--)
{
head=tail=0;
node*root =new node();
scanf("%d",&n);
getchar();
while(n--)
{
gets(word);
//puts(word);
insert(root,word);
}
build_fail(root);
gets(words);
printf("%d\n",ac_auto(root));
}
return 0;
}
题目链接在此~
说下题目大意 给出case数,多组数据,每组数据中有n个子串,给出目标串,求出共有多少个子串在目标串中出现过,输出数量。
一开始的思路是运用hash,枚举子串长度,计算hash值,相等则答案加一。。妥妥的tle。。。
附上代码求大牛优化。。。
(就算不tle也有可能wa。。。)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char word[11000][60];
char x[1100000];
typedef unsigned long long ll;
ll hash[1100000][60];
bool hashs[1100000][60]={false};
ll xp[1100000];
ll hash1[1100000][60];
int base=31;
int N;
int lens;
bool fail=false;
ll gethash(char * a,ll hashr[][60])
{
fail=false;
int len=strlen(a);
int i;
hashr[len][0]=0;
for(i=len-1;i>=0;i--)
{
if(a[i]<'a'&&a[i]>'z')
{
fail=true;
break;
}
hashr[i][0]=hashr[i+1][0]*base+a[i]-'a'+1;
}
return hashr[0][0];
}
int find_sub()
{
int i;
int ans=0;
for(i=0;i<N;i++)
{
ll temp=gethash(word[i],hash1);
if(fail)
return 0;
// cout<<temp<<endl;
int length=strlen(word[i]);
//cout<<lens<<endl;
int j;
for(j=0;j<=lens-length;j++)
{
if(!hashs[j][length])
{
//cout<<1<<endl;
hash[j][length]=hash[j][0]-hash[j+length][0]*xp[length];
hashs[j][length]=true;
}
//cout<<i<<" "<<j<<" "<<hash[j]-hash[j+length]*xp[length]<<endl;
if(temp==hash[j][length])
{ans++;break;}
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
xp[0]=1;
for(int i=1;i<1100000;i++)
{
xp[i]=xp[i-1]*base;
}
while(T--)
{
memset(hashs,0,sizeof(hashs));
scanf("%d",&N);
int i;
for(i=0;i<N;i++)
{
scanf("%s",word[i]);
}
scanf("%s",x);
lens=strlen(x);
gethash(x,hash);
if(fail)
return 0;
printf("%d\n",find_sub());
}
return 0;
}
咳咳。步入正题。
因为运用hash多次超时,然后发现这道题被作为了ac自动机的模板题。。
当看到这个名字的时候别提多激动了。。。有了这个还比什么acm啊。。。ac自动啊。。。
当我得知真相的那一刻。。。唉。
ac自动机有三步,构建trie树,bfs构建fail指针,最后进行目标串匹配。
个人任务构建fail指针最难没有之一。。毕竟kmp还运用不熟练呢。。。
参考大牛代码作出如下ac自动机。。。。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int kind=26;
struct node{
node * fail;
node * next[kind];
int count;
node()
{
memset(next,0,sizeof(next));
count=0;
fail=NULL;
}
}*q[510000];//队列
char word[60];//子串
char words[1100000];//目标串
int head,tail;
void insert(node* root,char *x)//trie树插入操作
{
int i=0,index;
node* p=root;
while(x[i])
{
index=x[i]-'a';
if(p->next[index]==NULL)
p->next[index]=new node();
p=p->next[index];
i++;
}
p->count++;
}
void build_fail(node* root)//构建指针
{
int i;
root->fail=NULL;
q[head++]=root;
while(head!=tail)//队列
{
node* temp=q[tail++];//出队
node*p=NULL;
for(i=0;i<26;i++)//遍历
{
if(temp->next[i]!=NULL)
{
if(temp==root)//root的子节点fail为root
{
temp->next[i]->fail=root;
}
else{
p=temp->fail;
while(p!=NULL)
{
if(p->next[i]!=NULL)
{
temp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL)
{
temp->next[i]->fail=root;
}
}
q[head++]=temp->next[i];//入队
}
}
}
}
int ac_auto(node* root)//匹配。。
{
int i=0,c=0,index;
node*p=root;
while(words[i])
{
index=words[i]-'a';
while(p->next[index]==NULL&&p!=root)//失配转向fail指针
p=p->fail;
p=p->next[index];
p=(p==NULL)?root:p;
node* temp=p;
while(temp!=root&&temp->count)//重复匹配过程
{
c+=temp->count;
temp->count=0;
temp=temp->fail;
}
i++;
}
return c;
}
int main()
{
int n,cases;
scanf("%d",&cases);
while(cases--)
{
head=tail=0;
node*root =new node();
scanf("%d",&n);
getchar();
while(n--)
{
gets(word);
//puts(word);
insert(root,word);
}
build_fail(root);
gets(words);
printf("%d\n",ac_auto(root));
}
return 0;
}
相关文章推荐
- c语言实现hashmap(转载)
- Javascript SHA-1:Secure Hash Algorithm
- 理解php Hash函数,增强密码安全
- PowerShell中定义哈希散列(Hash)和调用例子
- Perl 哈希Hash用法之入门教程
- perl哈希hash的常见用法介绍
- php的hash算法介绍
- 简单的四则运算
- 数的奇偶性
- linux 内核 hash table 的使用
- perl socket传hash
- perl socket传hash(use Storable)
- 简单的hash查表原理
- ACM网址
- PHP一致性hash的实现
- 一步一步写算法(之hash表)
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 转载HASH