首页 > 其他分享 >P2292 [HNOI2004] L 语言

P2292 [HNOI2004] L 语言

时间:2022-08-30 14:03:54浏览次数:51  
标签:20 语言 int anss bj 405 HNOI2004 ans P2292

给出字典(个数为 \(n\))和文章(个数为 \(m\) ),求文章最大匹配前缀。\(n\leq 20,m\leq 50\) , \(|s|\leq 20, |t|\leq 2\times 10^6\)


首先想到用AC自动机,在每个字串结尾标记串的长度,即 \(bj[p]=slen\) 。构造一个 \(ans\) 数组, \(ans[i]=1\) 表示 \(ans[i]\) 之前都可以被理解。初值 \(ans[0]=1\) ,若在 \(trie\) 上匹配成功, \(ans[i]=ans[i] | ans[i-bj[p]]\) 。最后从尾到头扫描,第一次 \(ans[i]=1\) 时即为答案。

期望得分:85pt

#include<bits/stdc++.h>
using namespace std;


int n,m;
int trie[30][405],cnt,bj[405],fa[405],num[405],nxt[405],ans[2000005];
char s[2000005];
queue <int> q;

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%s",s);
		int slen=strlen(s);
		int p=0;
		for(int j=0;j<slen;++j)
		{
			if(trie[s[j]-'a'][p])
				p=trie[s[j]-'a'][p];
			else
			{
				cnt++;
				num[cnt]=s[j]-'a';
				fa[cnt]=p;
				trie[s[j]-'a'][p]=cnt;
				p=cnt;
			}
		}
		bj[p]=slen;
	}
	q.push(0);
	while(!q.empty())
	{
		int p=q.front();
		q.pop();
		for(int i=0;i<26;++i)
		{
			if(trie[i][p])
				q.push(trie[i][p]);
		}
		if(fa[p]==0)
			continue;
		int j=nxt[fa[p]];
		while(j && trie[num[p]][j]==0)
			j=nxt[j];
		if(trie[num[p]][j])
			j=trie[num[p]][j];
		nxt[p]=j;
	}
	for(int i=1;i<=m;++i)
	{
		scanf("%s",s+1);
		int slen=strlen(s+1);
		int p=0;
		ans[0]=1;
		for(int j=1;j<=slen;++j)
		{
			while(p && trie[s[j]-'a'][p]==0)
				p=nxt[p];
			if(trie[s[j]-'a'][p])
				p=trie[s[j]-'a'][p];
			int t=p;
			while(t)
			{
				ans[j]=ans[j] | ans[j-bj[t]];
				t=nxt[t];
			}
		}
		for(int j=slen;j>=0;--j)
		{
			if(ans[j])
			{
				printf("%d\n",j);
				break;
			}
		}
		memset(ans,0,sizeof(ans));
	}
	return 0;
}

再考虑正解,加强后AC自动机要跑 \(2\times10^6\times50=10^8\) 次,如果跳跃的次数太多,不可能不超时。

考虑到子串长度 \(<=20\) ,可以尝试用状压来做(事实上最后使用的是bitset),具体做法:将 \(bj\) 数组记为跳跃之后可以达到的长度集,在 \(bfs\) 求 \(fail\) 指针时,每当找到一个点 \(p\) 的 \(nxt\) 指针,立刻让 \(bj[p]=bj[p] | bj[nxt[p]]\) ,这样在求 \(fail\) 指针的同时也求出了完整的 \(bj\) 集。

定义一个 \(anss\) ,记为在最近20个字符中 \(ans=1\) 的字符集( \(ans\) 定义与上同),那么在长串匹配时如果 \(anss\&bj[p]!=0\) ,则表示现在匹配的 \(ans=1\) ,加入 \(anss\) 集,同时删除 \(anss\) 中超过20的部分,是常数级运算。

在真正实现时,用 \(bitset\) 的左移来模拟 \(anss\) 中删除的操作。100pt!

#include<bits/stdc++.h>
using namespace std;


int n,m;
int trie[30][405],cnt,fa[405],num[405],nxt[405],lst;
char s[2000005];
bitset <25> anss;
bitset <25> bj[405];

queue <int> q;

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%s",s);
		int slen=strlen(s);
		int p=0;
		for(int j=0;j<slen;++j)
		{
			if(trie[s[j]-'a'][p])
				p=trie[s[j]-'a'][p];
			else
			{
				cnt++;
				num[cnt]=s[j]-'a';
				fa[cnt]=p;
				trie[s[j]-'a'][p]=cnt;
				p=cnt;
			}
		}
		bj[p][slen]=1;
	}
	q.push(0);
	while(!q.empty())
	{
		int p=q.front();
		q.pop();
		for(int i=0;i<26;++i)
		{
			if(trie[i][p])
				q.push(trie[i][p]);
		}
		if(fa[p]==0)
			continue;
		int j=nxt[fa[p]];
		while(j && trie[num[p]][j]==0)
			j=nxt[j];
		if(trie[num[p]][j])
			j=trie[num[p]][j];
		nxt[p]=j;
		bj[p]=(bj[p] | bj[j]);
	}
	for(int i=1;i<=m;++i)
	{
		scanf("%s",s+1);
		int slen=strlen(s+1);
		int p=0;
		anss[0]=1;
		lst=0;
		int j;
		for(j=1;j<=slen;++j)
		{
			anss<<=1;
			while(p && trie[s[j]-'a'][p]==0)
				p=nxt[p];
			if(trie[s[j]-'a'][p])
				p=trie[s[j]-'a'][p];
			if((anss&bj[p])!=0)
			{
				anss[0]=1;
				lst=j;
			}
			if(j-lst>20)
				break;
		}
		printf("%d\n",lst);
		anss.reset();
	}
	return 0;
}

标签:20,语言,int,anss,bj,405,HNOI2004,ans,P2292
From: https://www.cnblogs.com/zhouzizhe/p/16639048.html

相关文章

  • R语言中seq函数
     001、seq(10)seq(2,10,2)##设置起始位置,步长  002、seq(2,10,length=2)##设置返回值的个数seq(2,10,le......
  • pycharm2022.2.1版本设置中文语言
    进入"File......
  • go语言数据I/O对象及操作
    概念在Go语言中,几乎所有的数据结构都围绕接口展开,接口是Go语言中所有数据结构的核心。在实际开发过程中,无论是实现web应用程序,还是控制台输入输出,又或者是网络操作,都不......
  • go语言接口详解
    go语言实现接口的条件如果一个任意类型T的方法集为一个接口类型的方法集的超集,则我们说类型T实现了此接口类型。T可以是一个非接口类型,也可以是一个接口类型。实现......
  • go语言的错误处理(自定义错误类型, wrap error)
    go语言的错误处理没有其他语言的try,catch,finally异常捕获机制,需要显式地进行错误处理,如果只是单纯地将错误返回,在深度过大时可能无法清楚地知道调用的链路。这时候可以通......
  • 你好大语言模型
    你好大语言模型使用GoogleColab首次涉足Bloom作为我的硕士课程Capstone项目的一部分,我将探索如何实现大型语言模型(LLM)。我确信这种探索会产生许多博客文章,但对......
  • C语言会员卡计费系统
    C语言会员卡计费系统程序设计题四:会员卡计费系统1问题描述设计一个会员卡计费管理系统,要求对会员进行分级管理,根据会员的级别享受不同的优惠政策。通过此课题,熟练掌......
  • C语言后缀表达式求值
    C语言后缀表达式求值从控制台输入一合法的后缀表达式,其中的运算符只包括+、一、*、/,运算数都是大于等于o的整数(除数不为零),按要求输出计算结果,或输出计算结和相对应的中缀......
  • C语言怎么给函数添加形参的默认值
    以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16637890.html如果不是机缘巧合,当年转到C++之后,恐怕很难再有机......
  • C语言 紧跟printf之后的while(1)
    当printf()后面紧跟while(1)时,printf的内容有时候能打印出来,有时候打印不出来原因是,printf()中的内容在缓冲区里,你得加上‘\n’或者使用fflush(stdout)才能让它......