题面传送门

AC 自动机有时只是辅助建图的工具,真的

首先看到多串问题,果断建出 AC 自动机。设 \(m=\sum|s_i|\)。

不难发现子串的包含关系构成了一个偏序集,于是我们考虑转化为图论,若 \(s_j\) 包含于 \(s_i\) 则连一条 \(i\to j\) 的边。显然利用 AC 自动机可实现 \(\mathcal O(m)\) 建图。

题目要我们求的实际上是该偏序集的最大反链大小,根据 Dilworth 定理可将其转化为最小可相交覆盖的大小。

而最小可相交链覆盖的大小又可以通过传递闭包转化为最小不可相交链覆盖的问题,最小不可相交问题又可通过拆点二分图求出。故第一问答案就是 \(n-\) 拆点二分图最大匹配,这个想怎么搞怎么搞,网络流、匈牙利皆可(然鹅 wtcl 不会匈牙利只好跑网络流了)。

至于输出方案……这个嘛,考虑我们当时求最小边覆盖是如何构造方案的,就一遍 DFS 求出源点能到达的点,那么最小边覆盖就是二分图左部不能到达的点 \(+\) 二分图右部能到达的点。最大独立集就求个补集就行了。

值得注意的一点是此题 \(m\) 高达 \(10^7\),递归显然会爆栈,故不能通过建出 fail 树并在 fail 树上一遍 DFS 实现建图。考虑在求 fail 数组的时候再记录一个 \(pos_i\) 表示 \(i\) 在 fail 树的祖先中离它最近的是某个串结尾位置的节点,建图的时候就枚举字符串 \(s_i\) 并遍历根到 \(s_i\) 结尾位置的路径上所有点,若发现某个点的 \(pos\) 值非零就连一条 \(i\to pos_x\) 的边,如果 \(fail_i\) 的 \(pos\) 值非零那也连一条 \(i\to pos_{fail_i}\) 的边,再 \(n^3\) 求遍传递闭包即可建出图来,正确性显然,并且巧妙地避开了递归爆栈的问题。

代码(荣 膺 最 劣 解):

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=750;
const int MAXLEN=1e7;
const int MAXV=1502;
const int MAXE=1.5e6;
const int INF=0x3f3f3f3f;
int n;string s[MAXN+5];
int ch[MAXLEN+5][2],fail[MAXLEN+5],pos[MAXLEN+5],ncnt=0;
bool d[MAXN+5][MAXN+5];
void insert(string s,int id){
int cur=0;
for(int i=0;i<s.size();i++){
if(!ch[cur][s[i]-'a']) ch[cur][s[i]-'a']=++ncnt;
cur=ch[cur][s[i]-'a'];
} pos[cur]=id;
}
void getfail(){
queue<int> q;
for(int i=0;i<2;i++) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<2;i++){
if(ch[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];q.push(ch[x][i]);
if(!pos[ch[x][i]]) pos[ch[x][i]]=pos[fail[ch[x][i]]];
} else ch[x][i]=ch[fail[x]][i];
}
}
}
int S=1501,T=1502;
int hd[MAXV+5],to[MAXE+5],cap[MAXE+5],nxt[MAXE+5],ec=1;
void adde(int u,int v,int f){
to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec;
to[++ec]=u;cap[ec]=0;nxt[ec]=hd[v];hd[v]=ec;
}
int dep[MAXV+5],now[MAXV+5];
bool getdep(){
memset(dep,-1,sizeof(dep));dep[S]=0;
queue<int> q;q.push(S);now[S]=hd[S];
while(!q.empty()){
int x=q.front();q.pop();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(!~dep[y]&&z){dep[y]=dep[x]+1;now[y]=hd[y];q.push(y);}
}
} return ~dep[T];
}
int getflow(int x,int f){
if(x==T) return f;int ret=0;
for(int &e=now[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z&&dep[y]==dep[x]+1){
int w=getflow(y,min(f-ret,z));
ret+=w;cap[e]-=w;cap[e^1]+=w;
if(f==ret) return ret;
}
} return ret;
}
int dinic(){
int ret=0;
while(getdep()) ret+=getflow(S,INF);
return ret;
}
bool vis[MAXV+5];
void dfs(int x){
if(vis[x]) return;vis[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z) dfs(y);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) cin>>s[i],insert(s[i],i);
getfail();
// for(int i=1;i<=ncnt;i++) printf("%d\n",pos[i]);
for(int i=1;i<=n;i++){
int cur=0;
for(int j=0;j<s[i].size();j++){
if(pos[cur]) d[i][pos[cur]]=1;
cur=ch[cur][s[i][j]-'a'];
} if(pos[fail[cur]]) d[i][pos[fail[cur]]]=1;
}
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
d[i][j]|=d[i][k]&d[k][j];
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
if(d[i][j]&&i!=j) adde(i,j+n,1);
}
for(int i=1;i<=n;i++) adde(S,i,1),adde(i+n,T,1);
printf("%d\n",n-dinic());dfs(S);vector<int> ans;
for(int i=1;i<=n;i++) if(vis[i]&&!vis[i+n]) ans.pb(i);
sort(ans.begin(),ans.end());ffe(it,ans) printf("%d ",*it);
return 0;
}

Codeforces 590E - Birthday(AC 自动机+Dilworth 定理+二分图匹配)的更多相关文章

  1. [BZOJ1143][CTSC2008]祭祀river(Dilworth定理+二分图匹配)

    题意:给你一张n个点的DAG,最大化选择的点数,是点之间两两不可达. 要从Dilworth定理说起. Dilworth定理是定义在偏序集上的,也可以从图论的角度解释.偏序集中两个元素能比较大小,则在图 ...

  2. Codeforces 163E(ac自动机、树状数组)

    要点 显然ac自动机的板子就可以暴力一下答案了 为了优化时间复杂度,考虑套路fail树的dfs序.发现本题需要当前这个尾点加上所有祖先点的个数,考虑使用树状数组差分一下,在父点+1,在子树后-1,每次 ...

  3. AC自动机——多个kmp匹配

    (并不能自动AC) 介绍: Aho-Corasick automaton,最经典的处理多个模式串的匹配问题. 是kmp和字典树的结合. 精髓与灵魂: ①利用trie处理多个模式串 ②引入fail指针. ...

  4. ac自动机暴力跳fail匹配——hdu5880

    很简单的题,ac自动机里再维护一个len表示每个状态的串长,用s去query时每到一个结点都要暴力跳fail,因为有可能这个结点不是,但是其fail是危险结点,找到一个就直接break 再用个差分数组 ...

  5. Codeforces 739D - Recover a functional graph(二分图匹配)

    Codeforces 题面传送门 & 洛谷题面传送门 首先假设我们已经填好了所有问号处的值怎样判断是否存在一个合法的构造方案,显然对于一种方案能够构造出合法的基环内向森林当且仅当: \(\fo ...

  6. Codeforces 547E - Mike and Friends(AC 自动机+树状数组)

    题面传送门 好久每做过 AC 自动机的题了--做几个题回忆一下罢 AC 自动机能够解决多串匹配问题,注意是匹配,碰到前后缀的问题那多半不在 AC 自动机能解决的范围内. 在初学 AC 自动机的时候相信 ...

  7. AC 自动机

    AC自动机(Aho-Corasick Automata)是经典的多模式匹配算法.从前我学过这个算法,但理解的不深刻,现在已经十分不明了了.现在发觉自己对大部分算法的掌握都有问题,决定重写一系列博客把学 ...

  8. HDU-4518 吉哥系列故事——最终数 AC自动机+数位DP

    题意:如果一个数中的某一段是长度大于2的菲波那契数,那么这个数就被定义为F数,前几个F数是13,21,34,55......将这些数字进行编号,a1 = 13, a2 = 21.现给定一个数n,输出和 ...

  9. UVa 11468 (AC自动机 概率DP) Substring

    将K个模板串构成一个AC自动机,那些能匹配到的单词节点都称之为禁止节点. 然后问题就变成了在Tire树上走L步且不经过禁止节点的概率. 根据全概率公式用记忆化搜索求解. #include <cs ...

随机推荐

  1. python flask1

    以这个服务端代码为例,简单了解一下flask的运用. 1.app = Flask(__name__)记住就好了 2.@app.route("/")记住就好了:注意括号里的是调用这个 ...

  2. 如果你还不知道Apache Zookeeper?你凭什么拿大厂Offer!!

    很多同学或多或少都用到了Zookeeper,并知道它能实现两个功能 配置中心,实现表分片规则的统一配置管理 注册中心,实现sharding-proxy节点的服务地址注册 那么Zookeeper到底是什 ...

  3. 【UE4 设计模式】命令模式 Command Pattern

    概述 描述 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化:对请求排队或者记录请求日志,以及支持可撤销的操作. 命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务 ...

  4. k8s replicaset controller分析(2)-核心处理逻辑分析

    replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...

  5. js判断移动端浏览器类型,微信浏览器、支付宝小程序、微信小程序等

    起因 现在市场上各种跨平台开发方案百家争鸣各有千秋,个人认为最成熟的还是hybird方案,简单的说就是写H5各种嵌入,当然作为前端工程师最希望的也就是公司采用hybird方案当作技术路线. 所谓的hy ...

  6. [技术博客] BeautifulSoup4分析网页

    [技术博客] BeautifulSoup4分析网页 使用BeautifulSoup4进行网页文本分析 前言 进行网络爬虫时我们需要从网页源代码中提取自己所需要的信息,分析整理后存入数据库中. 在pyt ...

  7. str数组

  8. 色彩滤镜矩阵(Color Filter Array)

    数码相机上的每个象素都带有一个光感应器,用以测量光线的明亮程度.由于光电二极管是只支持单颜色的装置,它不能区别不同波长的光线.因此,数码相机工程师在相机感应器的上部装上了一套镶嵌式的颜色滤镜,一个颜色 ...

  9. Exynos4412 中断处理流程详解

    Linux 中,当外设触发中断后,大体处理流程如下: a -- 具体CPU architecture相关的模块会进行现场保护,然后调用machine driver对应的中断处理handler; b - ...

  10. 2021 CCPC女生赛

    newbie,A了五题铜牌收工 比赛时和队友悠哉游哉做题,想着干饭,最后幸好没滚出铜尾. 贴一下比赛过的代码 A题 签到 队友A的,判断正反方向序列是否符合要求 /*** * @Author: _Kr ...