HDOJ 题目4787 GRE Words Revenge(在线ac自动机,离线也可做)

本文解析了一道关于GRE词汇复习的算法题,通过构造自动机实现单词的学习与查询功能,涉及字符串处理、自动机构建及合并等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GRE Words Revenge

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others)
Total Submission(s): 1570    Accepted Submission(s): 352


Problem Description
  Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. At each day, Coach Pang can:
   "+w": learn a word w
   "?p": read a paragraph p, and count the number of learnt words. Formally speaking, count the number of substrings of p which is a learnt words.
  Given the records of N days, help Coach Pang to find the count. For convenience, the characters occured in the words and paragraphs are only '0' and '1'.
 

Input
  The first line of the input file contains an integer T, which denotes the number of test cases. T test cases follow.
  The first line of each test case contains an integer N (1 <= N <= 10 5), which is the number of days. Each of the following N lines contains either "+w" or "?p". Both p and w are 01-string in this problem.
  Note that the input file has been encrypted. For each string occured, let L be the result of last "?" operation. The string given to you has been shifted L times (the shifted version of string s 1s 2 ... s k is s ks 1s 2 ... s k-1). You should decrypt the string to the original one before you process it. Note that L equals to 0 at the beginning of each test case.
  The test data guarantees that for each test case, total length of the words does not exceed 10 5 and total length of the paragraphs does not exceed 5 * 10 6.
 

Output
  For each test case, first output a line "Case #x:", where x is the case number (starting from 1).
  And for each "?" operation, output a line containing the result.
 

Sample Input
  
2 3 +01 +01 ?01001 3 +01 ?010 ?011
 

Sample Output
  
Case #1: 2 Case #2: 1 0
 

Source
 

Recommend
We have carefully selected several similar problems for you:   5368  5367  5366  5365  5364 
题目大意:+w表示学了一个单词,?p问学了单词的数量(第一,单词是不同的,第二,算的是所有在串中出现的数量,不是存在的单词的数量)
题意确实开始理解错了,
例如
+10
+10
?101
应该是1
+10
?101010
应该是3
然后文字是加密的,上一个求出的值,后边的字符串有像左移这么多位,不管是+还是?的!!!
思路:开始就像用暴力点的,离线做,直接就是模板,,一直re,因为重插入了之后又要重新build_ac一回,确实暴力了点,后来百度看人家都是都是用两个ac自动机实现的在线ac自动机,然后就学了一下,大概思想是这样的就是设两个的ac自动机,一个大的一个小的,小的就相当于缓存,先往小的存。小的有个上限(上限应该是最大节点数的开方,,不过我试了几个数发现差不太多),达到上限之后就往大的ac自动机上合并,合并比较经典。。。具体看代码,,,就这样实现在线查询,,,在修改在线的代码是,发现shift函数写错了,,改过了就过,突然想起来离线的代码可能就是因为这个re的,,然后改了之后wa了,,没超时,,顿时激动了,,改了改也过了,,哈哈,离线也能做
ac代码(在线ac自动机)
注释的那种写法也行算vis
ac代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#pragma comment(linker, "/STACK:1024000000,1024000000")
char str[5000100],s1[5000100];
int head,tail;
int node_num;
struct node  
{  
    node *fail;  
    node *next[2];  
    __int64 vis,key;  
    node()  
    {  
        fail=NULL;  
        key=vis=0;  
        for(int i=0;i<2;i++)  
            next[i]=NULL;  
    }  
}*q[8008000];  
node *root1,*root2;		
void insert(char *s,node *root)  
{  
    int temp,len,i;  
    node *p=root;  
    len=strlen(s);  
    for(i=0;i<len;i++)  
    {  
        temp=s[i]-'0';		
        if(p->next[temp]==NULL)
		{
			node_num++;
            p->next[temp]=new node();
		}
        p=p->next[temp];  
    }  
    p->vis=1;      
}  
void build_ac(node *root)  
{  
	head=tail=0;
    q[tail++]=root;
    while(head!=tail)  
    {  
        node *p=q[head++];  
        node *temp=NULL;  
        for(int i=0;i<2;i++)  
        {  
            if(p->next[i]!=NULL)  
            {  
                if(p==root)
				{
                    p->next[i]->fail=root;
					p->next[i]->key=p->next[i]->vis;
				}
                else  
                {  
                    temp=p->fail;  
                    while(temp!=NULL)  
                    {  
                        if(temp->next[i]!=NULL)  
                        {  
                            p->next[i]->fail=temp->next[i];  
							p->next[i]->key=temp->next[i]->key+p->next[i]->vis;
                            break;  
                        }  
                        temp=temp->fail;  
                    }  
                    if(temp==NULL)  
                    {  
                        p->next[i]->fail=root;
						p->next[i]->key=p->next[i]->vis;
                    }  
                }  
                q[tail++]=p->next[i];  
            }  
        }  
    }  
}  
__int64 query(char *str,node *root)  
{  
    __int64 ans=0;  
    int len=strlen(str);  
    node *p=root,*temp;  
    for(int i=0;i<len;i++)  
    {  
        int x=str[i]-'0';  
        while(p->next[x]==NULL&&p!=root)  
            p=p->fail;  
        p=p->next[x];  
        if(p==NULL)  
        {  
            p=root;  
        }  
        temp=p;  
       /*while(temp!=root)  
        {  
            ans+=temp->vis;  
            temp=temp->fail;  
       } */
		ans+=temp->key;
    }  
    return ans;
}
int seach(char *s,node *root)
{
	int len=strlen(s),i,j,now;
	node *cur=root;
	for(i=0;i<len;i++)
	{
		now=s[i]-'0';
		if(cur->next[now]!=NULL)
		{
			cur=cur->next[now];
		}
		else
			return 0;
	}
	if(cur->vis)
		return 1;
	return 0;
}
void del(node *root)
{
	if(root==NULL)
		return;
	int i;
	for(i=0;i<2;i++)
	{
		if(root->next[i]!=NULL)
			del(root->next[i]);
	}
	free(root);
}
void Union(node *root1,node *root2)//把ac自动机2合并到1上
{
	head=tail=0;
	q[tail++]=root1;
	q[tail++]=root2;
	int i,j;
	while(head!=tail)
	{
		node *r1=q[head++];
		node *r2=q[head++];
		for(i=0;i<2;i++)
		{
			if(r2->next[i]!=NULL)
			{
				if(r1->next[i]==NULL)
				{
					r1->next[i]=new node();
				}
				r1->next[i]->vis|=r2->next[i]->vis;
				q[tail++]=r1->next[i];
				q[tail++]=r2->next[i];
			}
		}
	}
}
void shilf(char *str,__int64 num)//字符串左移num
{
	int len=strlen(str);
	num%=len;
	num=len-num;
	if(!num)
		return;
	int i=0;
	for(i=0;i<num;i++)
	{
		s1[i]=str[len-num+i];
	}
	for(i=num;i<len;i++)
	{
		s1[i]=str[i-num];
	}
	for(i=0;i<len;i++)
	{
		str[i]=s1[i];
	}
	str[len]=0;
}
int main()
{
	int t,c=0;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		root1=new node();
		root2=new node();
		scanf("%d",&n);
		printf("Case #%d:\n",++c);
		int i;
		__int64 num=0;
		node_num=0;
		build_ac(root1);
		for(i=0;i<n;i++)
		{
			scanf("%s",str);
			if(str[0]=='+')
			{
				shilf(str+1,num);
				if(seach(str+1,root1)||seach(str+1,root2))
					continue;
				insert(str+1,root2);
				build_ac(root2);
				if(node_num>2500)
				{
					Union(root1,root2);
					del(root2);
					root2=new node();
					build_ac(root1);
					build_ac(root2);
					node_num=0;
				}
			}
			else
			{
				shilf(str+1,num);
			//	printf("%s\n",str+1);
				__int64 ans=query(str+1,root1)+query(str+1,root2);
				num=ans;
				printf("%I64d\n",ans);
			}
		}
		del(root1);
		del(root2);
	}
}
ac代码(离线ac自动机就)
#include<stdio.h>
#include<string.h>
char str[5000010],s1[5000010];
int head,tail;  
struct node  
{  
    node *fail;  
    node *next[2];  
    __int64 cnt;  
    node()  
    {  
        fail=NULL;  
        cnt=0;  
        for(int i=0;i<2;i++)  
            next[i]=NULL;  
    }  
}*q[8000800];  
node *root;  
void insert(char *s)  
{  
    int temp,len,i;  
    node *p=root;  
    len=strlen(s);  
    for(i=0;i<len;i++)  
    {  
        temp=s[i]-'0';  
        if(p->next[temp]==NULL)  
            p->next[temp]=new node();  
        p=p->next[temp];  
    }  
    p->cnt++;      
}  
void build_ac()  
{  
	head=tail=0;
    q[tail++]=root;  
    while(head!=tail)  
    {  
        node *p=q[head++];  
        node *temp=NULL;  
        for(int i=0;i<2;i++)  
        {  
            if(p->next[i]!=NULL)  
            {  
                if(p==root)  
                    p->next[i]->fail=root;  
                else  
                {  
                    temp=p->fail;  
                    while(temp!=NULL)  
                    {  
                        if(temp->next[i]!=NULL)  
                        {  
                            p->next[i]->fail=temp->next[i];  
                            break;  
                        }  
                        temp=temp->fail;  
                    }  
                    if(temp==NULL)  
                    {  
                        p->next[i]->fail=root;  
                    }  
                }  
                q[tail++]=p->next[i];  
            }  
        }  
    }  
}  
__int64 query(char *str)  
{  
    __int64 ans=0;  
    int len=strlen(str);  
    node *p=root,*temp;  
    for(int i=0;i<len;i++)  
    {  
        int x=str[i]-'0';  
        while(p->next[x]==NULL&&p!=root)  
            p=p->fail;  
        p=p->next[x];  
        if(p==NULL)  
        {  
            p=root;  
        }  
        temp=p;  
        while(temp!=root)//没有&&temp->cnt
        {  
			ans+=temp->cnt;  
			//temp->cnt=-1;
		//	temp->cnt=0;
            temp=temp->fail;  
        }  
    }  
    return ans;
}
void shift(char *str,__int64 num)
{
    int len=strlen(str);
    num%=len;
    num=len-num;
    if(!num)
        return;
    int i=0;
    for(i=0;i<num;i++)
    {
        s1[i]=str[len-num+i];
    }
    for(i=num;i<len;i++)
    {
        s1[i]=str[i-num];
    }
    for(i=0;i<len;i++)
    {
        str[i]=s1[i];
    }
}
int seach(char *s)
{
	int len=strlen(s),i,j,now;
	node *cur=root;
	for(i=0;i<len;i++)
	{
		now=s[i]-'0';
		if(cur->next[now]!=NULL)
		{
			cur=cur->next[now];
		}
		else
			return 0;
	}
	if(cur->cnt)
		return 1;
	return 0;
}
int main()
{
    int t,c=0;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        root=new node();
        scanf("%d",&n);
		printf("Case #%d:\n",++c);
        int i;
        __int64 num=0;
        for(i=0;i<n;i++)
        {
            scanf("%s",str);
            if(str[0]=='+')
            {
				shift(str+1,num);
				if(seach(str+1))
					continue;
                insert(str+1);
            }
            else
            {
                shift(str+1,num);
                build_ac();
                __int64 ans=query(str+1);
                num=ans;
                printf("%I64d\n",ans);
            }
        }
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值