BZOJ 1212 [HNOI2004]L語言 【AC自動機 + 背包】
阿新 • • 發佈:2017-10-01
har trie 公共前綴 語言 const highlight 復雜度 題解 end
題目鏈接【http://www.lydsy.com/JudgeOnline/problem.php?id=1212】
題意:給你一些單詞,然後給出一個沒有標點的文本串S,都是小寫字符。現在讓你求用給出的單詞組成文本串T,求S和T的最長公共前綴。
題解:AC自動機 + 背包,背包dp[i],表示是否能組成長度為【1,i】的前綴,在自動機中維護Len[i],表示第i個節點到根節點的距離,End[i],節點i是否是某個單詞的結尾。在查詢的時候,我們只需要在對應的Trie上跳就可以了,時間復雜度為x * N*log(N)。
#include<bits/stdc++.h> using namespace std; const int maxn = 1024 * 1024 + 15; int dp[maxn]; struct Aho_C { int Next[maxn][26], Fail[maxn], End[maxn], Len[maxn]; int root, sz; int newnode() { for(int i = 0; i < 26; i++) Next[sz][i] = -1; End[sz++] = 0; return sz - 1; } void init() { sz = 0; root = newnode(); } void Insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0; i < len; i++) { if(Next[now][buf[i] - ‘a‘] == -1) Next[now][buf[i] - ‘a‘] = newnode(); now = Next[now][buf[i] - ‘a‘]; Len[now] = i + 1; } End[now]++; } void Build() { queue<int>Q; Fail[root] = root; for(int i = 0; i < 26; i++) if(Next[root][i] == -1) Next[root][i] = root; else { Fail[Next[root][i]] = root; Q.push(Next[root][i]); } while( !Q.empty() ) { int now = Q.front(); Q.pop(); for(int i = 0; i < 26; i++) if(Next[now][i] == -1) Next[now][i] = Next[Fail[now]][i]; else { Fail[Next[now][i]] = Next[Fail[now]][i]; Q.push(Next[now][i]); } } } void Query(char buf[]) { int len = strlen(buf + 1); int now = root; for(int i = 1; i <= len; i++) { dp[i] = 0; now = Next[now][buf[i] - ‘a‘]; int temp = now; int tmp = Len[temp]; while( temp != root) { if(End[temp]) { int pos = i - Len[temp]; dp[i] = max(Len[temp] + dp[pos], dp[i]); } temp = Fail[temp]; } } } } ac; char buf[maxn * 2]; int main() { int N, M; scanf("%d %d", &N, &M); ac.init(); for(int i = 1; i <= N; i++) { scanf("%s", buf); ac.Insert(buf); } ac.Build(); for(int i = 1; i <= M; i++) { scanf("%s", buf + 1); ac.Query(buf); int len = strlen(buf + 1); int ma = 0; for(int i = len; i >= 1; i--) { if(dp[i] == i) { ma = i; break; } } printf("%d\n", ma); } return 0; }
BZOJ 1212 [HNOI2004]L語言 【AC自動機 + 背包】