萌新感言:
我的天吶!
因為是ac自動機的專題所以沒有管別的。。。硬著頭皮吃那份題解(**)。。【請戳簡單美麗可愛的**(沒開玩笑)】
首先講ac自動機:
tag存的是以這個節點為字尾的字串個數(已狀壓)。
我們在構造fail指標的時候,採用的是bfs的手段,對於樹而言,那就是一層一層的向下遍歷,
所以**很巧妙(不能說巧妙吧,就是這樣的),在找到fail位置的時候直接跳出,然後更新了tag,為什麼可以這樣?因為長的找到的時候,短的早就找過了,所以直接更新就好了。
然後這份**有乙個小瑕疵(講錯請吐槽!):因為在構造fail指標的時候tag已經更新過了,所以在dp的時候沒必要再找到fail指標然後更新。
自身問題(可跳過):
還有構造fail的函式中當節點不存在的時候,這個節點的位置,用父節點的這個元素的fail指標取代了,如圖:
道理還是一樣,我要保證長的找到的時候,短的早就找過了。
然後講dp:
感覺ac自動機下的dp很好理解,因為trie樹上本身對於每個節點就是一種種狀態,用bfs的手段從上層到下層遍歷。
dp[ i ][ j ]表示匹配到 i 節點,匹配到 j 個字串時的最短步數。
每次只會伸展乙個新節點,從而獲取更多的字尾串,所以
dp[x][new_string_num]=min(dp[x][new_string_num],dp[x的父節點][old_string_num]+1);
that's all,thanks for watching....
//#include #include#include#include#include#includeusing namespace std;
typedef pairpii;
const int n=205;
const int inf=0x3f3f3f3f;
int n,dp[n][1030];
int g[n][4],fail[n],tag[n];
int sz;
void init()
int getid(char x)
void ins(char *str,int id)
}while(!que.empty())
que.push(u);
int v=fail[root];
while(v && g[v][i]==0)
v=fail[v];
fail[u]=g[v][i]; //構造
tag[u]|=tag[fail[u]]; //更新節點存的字串個數。}}
}int solve()
{ //初始化
build_fail();
memset(dp,inf,sizeof(dp));
dp[0][0]=0;
queueque;
que.push(make_pair(0,0));//塞入根節點,匹配0;
while(!que.empty())
{int u=que.front().first;
int s=que.front().second;
que.pop();
for(int i=0;i<4;i++){
int k=g[u][i]; //匹配這個節點,因為之前當節點不存在的時候已經存了父節點的該元素的fail指標,所以不用考慮為空
int ss=s|tag[k]; //在建立fail指標的時候,tag[k]存的字串個數已經更新
if(dp[k][ss]>dp[u][s]+1)
{dp[k][ss]=dp[u][s]+1;
que.push(make_pair(k,ss));
if(ss==(1<
hdu 2825 AC自動機 狀壓dp
假設乙個字串長為n,現在在j這個位置,此時已經包含了乙個模式串集合 由於m只有10,所有可以用狀壓來表示模式串 到下乙個位置時,一共有26種情況,現在你想知道的是多乙個字母後會多幾個模式串,由於是多匹配問題便想到了ac自動機,而此時只要紀錄走道j這個位置時trie上走到k這個位置。所以狀態便是dp ...
HDU 2825 AC自動機 狀壓DP
給m個字串,要求組成乙個長度為n的字串,至少包含k個給定字串。利用ac自動機,我們可以進行狀態轉移,以及模板匹配。要求目標串長度為n,且包含k個給定字串。所以可以在包含給定字串的ac自動機上進行狀態轉移。dp i 1 u last u s dp i 1 u last u s dp i j s mod...
HDU 3247 AC自動機 狀壓DP
乙個n個資源串,m個病毒串。要求生成乙個字串,包含所有資源串,但不能包含病毒串。問生成字串的最小長度。首先利用ac自動機求出資源串的狀態轉移關係,因為資源串最多只有十種,因此可以進行狀態壓縮。在利用ac自動機求失配關係的時候 這裡在求失配關係的時候需要將不存在的邊補上 順便求出來狀態包含關係。求出失...