P2322 [HNOI2006]最短母串問題
阿新 • • 發佈:2018-09-22
ostream nod div noi 重復 tle 子串 pri math
傳送門
看到題面肯定先搞個AC自動機
考慮一位一位填字符
那麽在自動機上就是一位一位匹配
考慮什麽時候包含了所有子串
顯然是經過了所有的結束標記(當然包括fail上的)
最多只有11個單詞
考慮狀態壓縮
經過第 i 個單詞結尾就把狀態的第 i 位 | 1
然後就可以廣搜找了
因為擴展是從 A 到 Z
所以一旦找到了就是字典序最小的答案
但是存答案也有點麻煩,具體還是看代碼吧
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> using namespace std; const int N=1e5+7; const int M=2e6+7; int n; int c[N][27],pd[N],fail[N],cnt; char a[N]; inline void ins(int k) { int u=0,len=strlen(a); for(int i=0;i<len;i++) { int v=a[i]-‘A‘+1; if(!c[u][v]) c[u][v]=++cnt; u=c[u][v]; } pd[u]|=(1<<k);//註意是 ‘|=‘ 可能有重復的單詞 } queue <int> qa; void pre() { for(int i=1;i<=26;i++) if(c[0][i]) qa.push(c[0][i]); while(!qa.empty()) { int u=qa.front(); qa.pop(); for(int i=1;i<=26;i++) { int &v=c[u][i];if(!v) c[u][i]=c[fail[u]][i]; else { fail[v]=c[fail[u]][i]; pd[v]= (pd[v]|pd[fail[v]]);//把fail上的狀態也轉過來 qa.push(v); } } } } struct node{ int pos,v,id;//pos是AC機上的位置,v是當前狀態,id是編號 };//搜索的狀態 queue <node> qb; bool f[1007][1<<12];//記憶化數組,是否到過AC機上第i個點時狀態為j int from[M],b[M],tot;//存答案,from存編號為i時上一個位置的編號,b存當前編號時的字符 void print(int &x)//遞歸輸出答案 { if(!x) return; print(from[x]); printf("%c",b[x]+‘A‘-1); } void bfs() { qb.push((node){0,0,0}); while(!qb.empty()) { node x=qb.front(); qb.pop(); if(x.v==((1<<n)-1))//如果找到答案 { print(x.id);//輸出 return; } for(int i=1;i<=26;i++) { int &pos=c[x.pos][i],v=x.v|pd[pos]; if(f[pos][v]) continue;//記憶化 f[pos][v]=1; from[++tot]=x.id; b[tot]=i; qb.push((node){pos,v,tot}); } } } int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%s",a); ins(i-1); } pre(); bfs(); return 0; }
P2322 [HNOI2006]最短母串問題