题面传送门

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. rocketmq优雅停机往事

    1 时间追溯到2018年12月的某一天夜晚,那天我正准备上线一个需求完就回家,刚点下发布按钮,告警就响起,我擦,难道回不了家了?看着报错量只有一两个,断定只是偶发,稳住不要慌. 把剩下的机器发完,又出 ...

  2. 热身训练2 GCD

    题目描述 简要题意:  n个数字,a1,a2,...,an m次询问(l,r),每次询问需回答 1.gcd(al,al+1,al+2,...,ar);2.gcd(ax,ax+1,ax+2,...,ay ...

  3. AOP源码解析:AspectJExpressionPointcutAdvisor类

    先看看 AspectJExpressionPointcutAdvisor 的类图 再了解一下切点(Pointcut)表达式,它指定触发advice的方法,可以精确到返回参数,参数类型,方法名 1 pa ...

  4. linux c语言 rename的用法-rename() does not work across different mount points, even if the same file system is mounted on both

    最近在一个项目上执行文件的搬移功能时发现总是失败,临时录像文件存放于emmc的/tmp/目录下,当录像完成时候则调用rename企图将此文件搬到/mnt/sdcard/mmcblk1p1/(这是外置的 ...

  5. hdu 5093 Battle ships(二分图最大匹配)

    题意: M*N的矩阵,每个格子上是三个之一:*.o.#.                     (1 <= m, n <= 50) *:海洋,战船可以停在上面.      o:浮冰,战船 ...

  6. coreseek使用心得

    基本使用方法: D:\coreseek-4.1\bin\searchd -c D:\coreseek-4.1\etc\article.conf --stop 停止服务 D:\coreseek-4.1\ ...

  7. SpringCloud 2020.0.4 系列之 Gateway入门

    1. 概述 老话说的好:做人要有幽默感,懂得幽默的人才会活的更开心. 言归正传,今天我们来聊聊 SpringCloud 的网关组件 Gateway,之前我们去访问 SpringCloud 不同服务的接 ...

  8. ONVIF协议客户端

    前几天跟大家聊了一些关于ONVIF的一些基础知识,它的工作原理以及优势.今天安徽思蔷信息科技为带大家了解一下simpleonvif 百度云盘下载地址:链接:https://pan.baidu.com/ ...

  9. Oracle的主要组件和基本概念

    oracle 简介 oracle(甲骨文)公司 1977年,三人合伙创办(Software Development Laboratories,SDL) 1979年,更名为Relational Soft ...

  10. string类运用:特殊的翻译

    特殊的翻译 小明的工作是对一串英语字符进行特殊的翻译:当出现连续且相同的小写字母时,须替换成该字母的大写形式,在大写字母的后面紧跟该小写字母此次连续出现的个数:与此同时,把连续的小写字母串的左侧和右侧 ...