题目链接:https://codeforces.com/problemset/problem/590/E
给定 \(n\) 个仅包含 a,b
的字符串,保证它们两两不同。你需要去掉尽可能少的字符串,使得剩下的字符串中不存在某一个串是另一个串的子串。
\(n \le 750\),\(\sum_{i=1}^n |s_i| \le 10^7\)。
首先建出 AC 自动机,然后把每一个串都扔到上面跑匹配。注意如果每次暴力跳 fail 显然会 T。所以我们在跳 fail 的同时路径压缩一下,建出一张 DAG。
然后我们需要求的就是这张 DAG 的最长反链,可以证明一张 DAG 的最长反链等于它的最小可重链覆盖。先 Floyd 传递闭包,然后把每一个点拆成两个点 \(x_1,x_2\),如果 DAG 上 \(x\) 可以到 \(y\),那么从 \(x_1\) 向 \(x_2\) 连边。
因为二分图最小可重链覆盖等于 \(n-\) 二分图最大匹配,所以我们先跑最大匹配即可得到最长反链大小。
然后考虑求出方案。我们先求出这张二分图的最小点覆盖,具体的,从每一个 \(x_2\) 开始,右边往左边只能走非匹配边,左边往右边只能走匹配边,把中途所有经过的点标记,这样右边没被标记的点和左边被标记的点的集合就是最小点覆盖。因为右边所有未匹配的点一定会被标记,右边匹配的点如果没有标记,左边的匹配点就一定会被标记。
然后最小点覆盖和最大独立集互为补集,所以这样右边被标记且左边没被标记的点就是最大独立集中的点了。
如果一个点拆出的两个点都在最大独立集中,那么这个点就是最长反链中的点。
时间复杂度 \(O(n^3+\sum m)\)。
#include <bits/stdc++.h>
using namespace std;
const int N=760,M=10000010;
int n,ans,tot,vis[N],head[N],pos[N],link[2][N];
bool dis[N][N],flag[2][N];
char s[M];
struct ACA
{
int tot,ch[M][3],fail[M],end[M],fa[M];
ACA() { tot=1; }
int ins(int i,char *s)
{
int len=strlen(s+1),p=1;
for (int j=1;j<=len;j++)
{
int id=s[j]-'a'+1;
if (!ch[p][id]) ch[p][id]=++tot,fa[tot]=p;
p=ch[p][id];
}
end[p]=i;
return p;
}
void build()
{
queue<int> q;
if (ch[1][1]) q.push(ch[1][1]),fail[ch[1][1]]=1;
if (ch[1][2]) q.push(ch[1][2]),fail[ch[1][2]]=1;
while (q.size())
{
int u=q.front(); q.pop();
if (ch[u][1]) fail[ch[u][1]]=ch[fail[u]][1],q.push(ch[u][1]);
else ch[u][1]=ch[fail[u]][1];
if (ch[u][2]) fail[ch[u][2]]=ch[fail[u]][2],q.push(ch[u][2]);
else ch[u][2]=ch[fail[u]][2];
}
}
void find(int i)
{
for (int j=pos[i];j>1;j=fa[j])
{
int x=fail[j],y=fail[j];
while (!end[x] && x>1) x=fail[x];
for (int z;y!=x;y=z)
z=fail[y],fail[y]=x;
fail[j]=x;
if (end[j] && end[j]!=i) dis[i][end[j]]=1;
if (x>1) dis[i][end[x]]=1;
}
}
}AC;
bool find(int x,int tag)
{
if (vis[x]==tag) return 0;
vis[x]=tag;
for (int v=1;v<=n;v++)
if (dis[x][v] && (!link[0][v] || find(link[0][v],tag)))
{
link[0][v]=x; link[1][x]=v;
return 1;
}
return 0;
}
void dfs(int x)
{
if (flag[0][x]) return;
flag[0][x]=1;
for (int v=1;v<=n;v++)
if (dis[x][v] && !flag[1][v])
{
flag[1][v]=1;
dfs(link[0][v]);
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s+1);
pos[i]=AC.ins(i,s);
}
AC.build();
for (int i=1;i<=n;i++)
AC.find(i);
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j && j!=k && i!=k)
dis[i][j]|=(dis[i][k]&dis[k][j]);
ans=n;
for (int i=1;i<=n;i++)
if (!vis[i]) ans-=find(i,i);
for (int i=1;i<=n;i++)
if (!link[1][i]) dfs(i);
printf("%d\n",ans);
for (int i=1;i<=n;i++)
if (flag[0][i] && !flag[1][i]) printf("%d ",i);
return 0;
}