洛谷CF590E Birthday(AC自動機)(最小路徑可重複點覆蓋方案)
阿新 • • 發佈:2018-12-20
題意
給你 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; }