• CF590E Birthday


    题意

    给定 (n) 个只由 (a,b) 组成的字符串,保证两两不同。

    要求从中选出尽可能多的字符串,使得选出的字符串中,任意一个字符串不是另一个的子串。

    求最多能选多少并输出一个可行解。

    (n leq 750, sum |S_i| leq 10^6)

    传送门

    思路

    考虑根据包含关系建边,可以得到一张有向无环图,之后我们要求的是一个最大点集,两两不能到达。这就是祭祀
    即求最长反链,然后转化为最小链覆盖,再到传递闭包后的最大匹配,关于证明,ta没了。
    接着来说一下如何输出方案。

    在求完传递闭包之后,能互相到达的点一定是相邻的,所以最长反链即为最大独立集。

    定义:最小点覆盖就是选择最少的点来覆盖所有的边。一个点能覆盖以它为端点的边
    最大独立集=所有顶点数-最小点覆盖

    突然写不下去了,就看这个吧偷懒,啥时候空了我来补

    然后来考虑怎么建图。
    由于是子串问题,很容易想到用AC自动机。
    但是如果把所有包含关系都求出来,妥妥的T了
    对于三个串 (a,b,c),如果 (a)包含(b) , (b)包含(c), 那么(a)包含(c)
    回忆AC自动机匹配的过程,对于文本串(i)每匹配到一个字符位置(u)就跳(fail),考虑到每次第一个跳到的一定是最长的,且每次跳的都是前面所有的子串,所以我们只要将(i)(fail[u])上包含的最长完整串连边就行。注意(fail[u])上可能不存在完整串末尾节点,那么我们就继承离他最近的(fail)树祖先上的串。
    不要忘记(u)本身是末尾节点的情况。
    至于怎么继承,只要在(get \_ fail)中加一句就好了

    #include <bits/stdc++.h>
    using std::string;
    using std::queue;
    queue <int> q;
    const int N=755,M=10000005;
    string s[N];
    int ch[M][2],val[M],fail[M],cnt,n,vis[N],now,to[N],len[N],f[N];
    int tagl[N],tagr[N];
    bool a[N][N];
    void insert(string s,int l,int id){
    	int u=0;
    	for (int i=0;i<l;i++){
    		int c=s[i]-'a';
    		if (!ch[u][c]) ch[u][c]=++cnt;
    		u=ch[u][c]; 
    	}
    	val[u]=id;
    }
    void get_fail(){
    	if (ch[0][0]) q.push(ch[0][0]);
    	if (ch[0][1]) q.push(ch[0][1]);
    	while (!q.empty()){
    		int x=q.front();
    		if (!val[x]) val[x]=val[fail[x]];
    		q.pop();
    		for (int i=0;i<2;i++)
    			if (ch[x][i]) {
    				q.push(ch[x][i]);
    				fail[ch[x][i]]=ch[fail[x]][i];
    			}else ch[x][i]=ch[fail[x]][i];
    	}
    }
    void match(string s,int l,int id){
    	int u=0;
    	for (int i=0;i<l;i++){
    		int c=s[i]-'a';
    		u=ch[u][c];
    		if (val[u]) a[id][val[u]]=1;
    		if (val[fail[u]]) a[id][val[fail[u]]]=1;
    	}
    }
    int dfs(int x){
    	for (int i=1;i<=n;i++)
    		if (a[x][i] && vis[i]!=now){
    			vis[i]=now;
    			if (f[i]==0 || dfs(f[i])){
    				to[x]=i,f[i]=x;
    				return 1;
    			}
    		}
    	return 0;
    }
    void dfs2(int x){
    	tagl[x]=1;
    	for (int i=1;i<=n;i++){
    		if (a[x][i]==0) continue;
    		if (!tagr[i]){
    			tagr[i]=1;
    			dfs2(f[i]);
    		}
    	}
    } 
    int main(){
    	scanf("%d",&n); 
    	for (int i=1;i<=n;i++){
    		std::cin>>s[i];
    		len[i]=s[i].length();
    		insert(s[i],len[i],i);
    	} 
    	get_fail();
    	for (int i=1;i<=n;i++)
    		match(s[i],len[i],i);
    	for (int k=1;k<=n;k++)
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				a[i][j]=a[i][j]|(a[i][k] & a[k][j]);
    	for (int i=1;i<=n;i++) a[i][i]=0;
    	int ans=0;
    	for (int i=1;i<=n;i++)
    		now=i,ans+=dfs(i);
    	printf("%d
    ",n-ans);
    	for (int i=1;i<=n;i++)
    		if (!to[i]) dfs2(i);
    	for (int i=1;i<=n;i++) 
    		if (tagr[i]==0 && tagl[i]) printf("%d ",i);
    }
    

    后记

    追随神仙的脚步
    争取做到(frac{1}{1 page})

  • 相关阅读:
    Testng Retry失败用例重新运行的方法(二)
    Testng Retry失败用例重新运行的方法(一)
    接口测试用例设计
    java 打印栈信息
    java 远程调试
    Java 字符串操作
    python中if __name__ == '__main__': 的解析
    Python OS模块介绍
    MAP/CAP信令常见消息
    Perl 获取当前系统时间
  • 原文地址:https://www.cnblogs.com/flyfeather6/p/12006783.html
Copyright © 2020-2023  润新知