BZOJ 1212 [HNOI2004]L语言 【AC自动机 + 背包】
题目链接【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; }
想的太多,做的太少。