1. 程式人生 > >P2322 [HNOI2006]最短母串問題

P2322 [HNOI2006]最短母串問題

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]最短母串問題