1. 程式人生 > >洛谷CF590E Birthday(AC自動機)(最小路徑可重複點覆蓋方案)

洛谷CF590E Birthday(AC自動機)(最小路徑可重複點覆蓋方案)

題意

給你 n 個字串,選出最大的一個集合,滿足兩兩之間不是對方的子串。

題解

AC自動機+最小路徑可重複點覆蓋方案=AC自動機+傳遞閉包+亂搞 求子串?KMP?這有我這種機智的人才會想到?AC自動機! AC自動機是用來處理字首的問題,看起來不適用,但它的fail指標太強大了!可以想象假設現在有一個串,它在trie樹中以一條鏈的形式儲存,從這條鏈中的每個點出去,擴充套件開來的就是它的一個子串。其實還是一個串字尾與一個串的字首的匹配,其中字尾指的是一個串的部分,字首要求是整串,那麼就是AC自動機啦~~~ 我們給一個串向它的子串連邊。 注意到AC自動機只要求出一個即可,後面傳遞閉包。 因為選了路徑開頭的一個點,後面一個也不能選,所以接下來求一個最小路徑可重複點覆蓋方案。 最小路徑可重複點覆蓋方案就懶得寫了,不會看

這篇部落格

程式碼

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int END=10000000;
const int MAXN=760,MAXL=10000010;

int n;
string s[MAXN];
bool ma[MAXN][MAXN];

struct Tr{int id,fail,son[2];}tr[MAXL];int root=1,tot=1;//空間??? 

void insert(int id)
{
    int x=root;
    for(int i=0,len=s[id].length();i<len;i++)
    {
        int k=s[id][i]-'a';
        if(!tr[x].son[k]) tr[x].son[k]=++tot;
        x=tr[x].son[k];
    }
    tr[x].id=id;
}

int head,tail,q[MAXL];
void get_fail()
{
    head=0,tail=0;
    for(int k=0,x=root;k<2;k++) if(tr[x].son[k])
    {
        int y=tr[x].son[k];
        tr[y].fail=root;
        q[tail++]=y;if(tail==END) tail=0;
    }
    while(head!=tail)
    {
        int x=q[head++];if(head==END) head=0;
        if(!tr[x].id) tr[x].id=tr[tr[x].fail].id;//debug
        for(int k=0;k<2;k++) if(tr[x].son[k])
        {
            int y=tr[x].son[k],p=tr[x].fail;
            while(p!=root && !tr[p].son[k])p=tr[p].fail;
            if(tr[p].son[k]) tr[y].fail=tr[p].son[k];
            else tr[y].fail=root;
            q[tail++]=y;if(tail==END) tail=0;
        }
    }
}

void solve(int id)
{
    int x=root;
    for(int i=0,len=s[id].length();i<len;i++)
    {
        int k=s[id][i]-'a';
        x=tr[x].son[k];
        if(tr[x].id!=id) ma[id][tr[x].id]=true;//id->tr[x].id
        else ma[id][tr[tr[x].fail].id]=true;
    }
}

int T=0,vis[MAXN];
int match[MAXN];
bool succ[MAXN];
bool dfs(int x)
{
    for(int y=1;y<=n;y++) if(ma[x][y] && vis[y]!=T)
    {
        vis[y]=T;
        if(!match[y] || dfs(match[y])) return match[y]=x,true;
    }
    return false;
}

int li[MAXN];
int main()
{
//     freopen("string.in","r",stdin);
//     freopen("string.out","w",stdout);
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        insert(i);
    }
    
    get_fail();
    for(int i=1;i<=n;i++) solve(i);
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) ma[i][j]|=ma[i][k]&ma[k][j];
    
    /*debug
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++) printf("%d",ma[i][j]);
        puts("");
    }
//    */
    
    int ans=n;
    for(int i=1;i<=n;i++)
    {
        T++;
        if(dfs(i)) ans--,succ[i]=true;
    }
    printf("%d\n",ans);
    
    for(int i=1,cnt=0;i<=n;i++) if(!succ[i]) li[++cnt]=i;
    T++;bool modify=true;
    while(modify)
    {
        modify=false;
        for(int i=1;i<=ans;i++)
            for(int j=1;j<=n;j++) if(ma[li[i]][j]) vis[j]=T;
        for(int i=1;i<=ans;i++)
            if(vis[li[i]]==T)
            {
                modify=true;
                while(vis[li[i]]==T) li[i]=match[li[i]];
            }
    }
    for(int i=1;i<=ans;i++) printf("%d ",li[i]);putchar('\n');
    return 0;
}