• BZOJ2938 [Poi2000]病毒 和 BZOJ5261 Rhyme


    [Poi2000]病毒

    二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

    示例:例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。

    任务:请写一个程序:

    1. 读入病毒代码;
    2. 判断是否存在一个无限长的安全代码;
    3. 将结果输出

    给出若干个 01 串,问是否存在一个无限长的 01 串,满足所有给出的串都不是它的子串.

    jklover的题解

    将给出的串插入到 AC 自动机里,那么若存在一个环,环上的节点及它们沿 fail 指针向上跳都不经过单词末节点,则符合要求.

    插入的时候将权值一起合并,最后做一次 dfs 即可.

    时间复杂度:线性。

    co int N=3e4+1,S=2;
    namespace AC
    {
    	int idx;
    	int ch[N][S],fail[N],val[N];
    	
    	void ins(char*s,int len)
    	{
    		int u=0;
    		for(int i=0;i<len;++i)
    		{
    			int k=s[i]-'0';
    			if(!ch[u][k])
    				ch[u][k]=++idx;
    			u=ch[u][k];
    		}
    		val[u]=1;
    	}
    	
    	void getfail()
    	{
    		std::queue<int>Q;
    		for(int i=0;i<S;++i)
    			if(ch[0][i])
    				Q.push(ch[0][i]);
    		while(Q.size())
    		{
    			int u=Q.front();Q.pop();
    			for(int i=0;i<S;++i)
    			{
    				if(ch[u][i])
    				{
    					fail[ch[u][i]]=ch[fail[u]][i];
    					val[ch[u][i]]|=val[ch[fail[u]][i]];
    					Q.push(ch[u][i]);
    				}
    				else
    					ch[u][i]=ch[fail[u]][i];
    			}
    		}
    	}
    	
    	int vis[N],inc[N];
    	
    	int dfs(int u)
    	{
    		inc[u]=1;
    		for(int i=0;i<S;++i)
    		{
    			int v=ch[u][i];
    			if(inc[v])
    				return 1;
    			if(vis[v]||val[v])
    				continue;
    			vis[v]=1;
    			if(dfs(v))
    				return 1;
    		}
    		inc[u]=0;
    		return 0;
    	}
    	
    	void solve()
    	{
    		puts(dfs(0)?"TAK":"NIE");
    	}
    }
    int n;
    char buf[N];
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	read(n);
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",buf);
    		AC::ins(buf,strlen(buf));
    	}
    	AC::getfail();
    	AC::solve();
    	return 0;
    }
    

    Rhyme

    由于多次交换邮票没有满足所有人的需求,小Z被赶出了集邮部。无处可去的小Z决定加入音乐部,为了让音乐部的人注意到自己的才华,小Z想写一首曲子。为了让自己的曲子更好听,小Z找到了一些好听曲子作为模板。曲谱可以表示成只包含小写字母的字符串,小Z希望自己最终的曲谱中任意一个长度为K的子串都是一个模板的子串。现在小Z想知道自己的曲谱最长可以是多长,如果可以无限长的话请输出INF。

    每组数据字符串总长不超过100000,1≤K≤100000。每个测试点数据不超过10组。

    dummyummy的题解

    可以先看一下这道题 [POI2000]病毒,虽然是个AC自动机,不过思路很像。

    对于这道题,我们只需要把广义SAM建出来,然后在那些只经过maxlen⩾k的结点的路径中选一个最长的就行了。最后一步可以用拓扑排序来完成。

    拓扑建边时可以直接向fail连边,而不是把儿子补全(像AC自动机那样ch[u][c]=ch[fail[u]][c]),这样能降低复杂度。

    最后如果出现环,就输出INF,否则求最长路径,注意特判所有结点的maxlen都小于k的情况,题目最下方有说明。

    时间复杂度(O(sum |s|))

    typedef pair<int,int> pii;
    
    co int N=2e5+3;
    int n,k;
    char str[N];
    int tot;
    int ch[N][26],fa[N],len[N];
    vector<pii> g[N];
    int ind[N],dis[N];
    void clear(){
    	for(int i=1;i<=tot+2;++i){
    		memset(ch[i],0,sizeof ch[i]);
    		g[i].clear(),ind[i]=dis[i]=0;
    	}
    	tot=1;
    }
    int extend(int p,int c){
    	int last;
    	if(ch[p][c]){
    		int q=ch[p][c];
    		if(len[q]==len[p]+1) last=q;
    		else{
    			int clone=last=++tot;
    			memcpy(ch[clone],ch[q],sizeof ch[q]);
    			fa[clone]=fa[q],len[clone]=len[p]+1;
    			fa[q]=clone;
    			for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    		}
    	}
    	else{
    		int cur=last=++tot;
    		len[cur]=len[p]+1;
    		for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    		if(!p) fa[cur]=1;
    		else{
    			int q=ch[p][c];
    			if(len[q]==len[p]+1) fa[cur]=q;
    			else{
    				int clone=++tot;
    				memcpy(ch[clone],ch[q],sizeof ch[q]);
    				fa[clone]=fa[q],len[clone]=len[p]+1;
    				fa[cur]=fa[q]=clone;
    				for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    			}
    		}
    	}
    	return last;
    }
    void Rhyme(){
    	clear();
    	for(int i=1;i<=n;++i){
    		scanf("%s",str+1);
    		int len=strlen(str+1),last=1;
    		for(int j=1;j<=len;++j) last=extend(last,str[j]-'a');
    	}
    	int s=tot+1,t=tot+2;
    	for(int i=1;i<=tot;++i){
    		if(len[i]==k-1){
    			for(int j=0;j<26;++j)if(ch[i][j]) g[i].push_back(pii(ch[i][j],1)),++ind[ch[i][j]];
    			g[s].push_back(pii(i,len[i])),++ind[i];
    		}
    		else if(len[i]>=k){
    			for(int j=0;j<26;++j)if(ch[i][j]) g[i].push_back(pii(ch[i][j],1)),++ind[ch[i][j]];
    			if(fa[i]&&len[fa[i]]>=k-1) g[i].push_back(pii(fa[i],0)),++ind[fa[i]];
    			g[i].push_back(pii(t,0)),++ind[t];
    			g[s].push_back(pii(i,len[i])),++ind[i];
    		}
    	}
    	if(!ind[t]) return printf("%d
    ",k-1),void();
    	int ans=0;
    	queue<int> q;q.push(s);
    	for(int u;!q.empty();q.pop()){
    		u=q.front(),ans=max(ans,dis[u]);
    		for(int i=0,v;i<g[u].size();++i){
    			v=g[u][i].first,dis[v]=max(dis[v],dis[u]+g[u][i].second);
    			if(!--ind[v]) q.push(v);
    		}
    	}
    	for(int i=1;i<=tot;++i)if(ind[i]) return puts("INF"),void();
    	printf("%d
    ",ans);
    }
    int main(){
    	while(~scanf("%d %d",&n,&k)) Rhyme();
    	return 0;
    }
    
  • 相关阅读:
    linux I2C 读写 tlv320dac3100
    ubuntu lfs
    安装和使用花生壳(linux)
    vim 配置
    vim
    gnome2 恢复默认 panel
    ubuntu 挂在 jffs2 文件
    gstreamer 播放
    gstreamer 环境变亮设置
    探讨【IGE】的源代码【五】。
  • 原文地址:https://www.cnblogs.com/autoint/p/10899121.html
Copyright © 2020-2023  润新知