您的位置:首页 > 其它

NotOnlySuccess 自动机专辑,完全题解 - by ronaflx

2011-09-15 21:07 375 查看
hdu 2222模板题,测试专用,测烂为止。

hdu 2896模板题。

hdu 3065水,查询稍微改一下貌似就OK

zoj 3430 需要解码……由于是个base64,所以解码需要用unsigned char 而且要算出解码的长度……某此月赛题,表示不会解码看shi个报告。

poj 2778 矩阵乘法,注意以自动机上的节点表示状态,每一个状态对应一个输入都会有下一状态(就是原来query里那么写的)。然后构建转移矩阵,用快速幂转移,略卡常数,模数很小,矩阵乘法中间不要取模。

hdu 2243 同样为矩阵乘法,模数为1 << 64直接用ULL 溢出就相当于取模了…… 1 + 26^1 + …… + 26^n 减去不含字串的。sumpow写错了……调自动机调了好久……

poj 1625 简单自动机DP,恶心的是需要大数……

hdu 2825 状态压缩自动机DP,很好的题目,状态为[当前串长度,自动机上的状态,二进制表示包换那些字串]。卡常数,需要把所有自动机中的下一状态预先处理出来,每个状态含那些串也处理出来,注意转移顺序。才能AC

hdu 2296 最大值的字典序最小,高效的方式是把串都反过来,然后插入自动机从后向前转移。但是直接暴力用string保存转移的最小字典序性价比很高……

poj 3691 基础的自动机DP 入门推荐级别题目

hdu 3341 恶题……变进制hash 一下 然后DP,状态数最多11^4 * 自动机状态。出题人就不能给个小点的范围吗……状态表示为[A][C][T][G][自动机状态]ACTG表示对已字符的个数,这样就可以不用长度来表示了,然后最大是11^4,考虑极端情况40 0 0 0 不能开一个41 * 41 * 41 * 41*状态数的数组,所以用变进制压缩ACTG就可以了……

zoj 3228 对于不可重叠的字符串记录最后一次出现的位置即可。然后一次查询统计。

hdu 3247 如果以自动机位状态用BFS转移的话, 复杂度太高,所以我们换一种思路,对于每个字符串要包含的文本,如果是结尾的话,必定在自动机的固定的状态上,然后我们把root和n个文本的结尾作为节点bfs预处理两点间的最短路,然后问题就转化成了TSP问题了,复杂度将为(S(自动机状态) * (n + 1)) + (n + 1)^2 * 2^n的复杂度。可以秒杀之。

zeroonlinejudge的空罐问题。如果不用长度,只用自动机做状态,要把所有可能出现的字符串插入到自动机中4^100的状态,显然不行……(也就我这么蒟蒻,开始这么想),所以状态上要加上长度。

状态 dp[i][j][k]第i天长度为j在自动机k状态的个数。

增长的问题很水,就是用单纯的自动机转移,用goStatus做转移,对于字符串变短的就用fail转移,然后每个状态给一个深度,分类讨论一下:

当j == 1的时候减少,就是是回收的字符串增减。

如果j > k->depth ,那么状态k是不变的。因为在开始减少字符不会导致向fail转移,字符串的后缀还是在k状态。

j <= k->depth的话,就转移到k->fail的状态。

贴上最后这题的代码+我们的模板

AC自动机好题

#pragma comment(linker, "/STACK:16777216")
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <iterator>
#include <limits>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <climits>
#include <algorithm>
#include <functional>
#include <numeric>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <bitset>
#include <list>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <stdexcept>
#include <utility>
#include <cassert>
#include <complex>
using namespace std;

#define LEFT(i) ((i) << 1)
#define RIGHT(i) (((i) << 1) | 1)
#define MID(i) ((l[i] + r[i]) >> 1)
#define CC(i, v) memset(i, v, sizeof(i))
#define REP(i, l, n) for(int i = l;i < int(n);++i)
#define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
#define DEBUG(i) cout << #i << " " << i << endl;
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef unsigned long long ULL;
#define code(ch) ((ch) - 'a')
const int KIND = 4;
const int MAXN = 1600;
struct node
{
node* next[KIND], *fail;
int count, depth;
bool valid;
} pool[MAXN], *pp, *root, *q[MAXN];
int head, tail;
node *newNode()
{
pp->fail = NULL;
pp->count = 0;
pp->valid = true;
memset(pp->next, 0, sizeof (pp->next));
return pp++;
}
void initialize()
{
pp = pool;
root = newNode();
}
node* insert(char *str)
{
node *now = root;
while (*str)
{
int idx = code(*str);
if (now->next[idx] == NULL)
now->next[idx] = newNode();
now = now->next[idx];
str++;
}
now->count++;
return now;
}
void buildFail(node* now, int ith)//build now's ith son
{
if(now == root) now->next[ith]->fail = root;
node* tmp = now->fail;
while(tmp)
{
if(tmp->next[ith] != NULL)
{
now->next[ith]->fail = tmp->next[ith];
return;
}
tmp = tmp->fail;
}
if(tmp == NULL) now->next[ith]->fail = root;
}
void build()
{
head = tail = 0;
q[tail++] = root;
while (head != tail)
{
node *beg = q[head++];
for (int i = 0; i < KIND; i++)
{
if (beg->next[i] == NULL) continue;
buildFail(beg, i);
q[tail++] = beg->next[i];
}
}
}
bool reachEnd(node* now)
{
while(now != root)
{
if(now->count) return true;
now = now->fail;
}
return false;
}
node* goStatus(node* now, int ith)
{
while(now->next[ith] == NULL && now != root)
now = now->fail;
now = now->next[ith];
return now == NULL ? root : now;
}

void calDepth(node* now, int depth)
{
if(now == NULL) return;
now->depth = depth;
for(int i = 0; i < KIND; i++)
calDepth(now->next[i], depth + 1);
}
const int N = 105;
const int MOD = 10007;
char str
, nowStr
;
int dp[2][N + 1][MAXN];

void add(int& now, int ad)
{
now += ad;
if(now >= MOD) now -= MOD;
}
pair<int, int> DP(char* nowStr, int day, int n)
{
node* now = root;
memset(dp, 0, sizeof(dp));
for(int i = 0; nowStr[i]; i++)
{
now = goStatus(now, code(nowStr[i]));
if(reachEnd(now)) return make_pair(0, 1);
}
dp[0][strlen(nowStr)][now - pool] = 1;
int ans1 = 0, ans2 = 0;
for(int i = 0; i < day; i++)
{
memset(dp[1], 0, sizeof(dp[1]));
for(int j = 1; j < N; j++)
{
for(node* k = pool; k < pp; k++)
{
int s = k - pool;
if(dp[0][j][s] == 0) continue;
//fail转移,字符串变短
if(j == 1)
add(ans1, dp[0][j][s]);
else if(j <= k->depth)
add(dp[1][j - 1][k->fail - pool], dp[0][j][s]);
else
add(dp[1][j - 1][s], dp[0][j][s]);
//next转移
for(int nxt = 0; nxt < KIND; nxt++)
{
now = goStatus(k, nxt);
if(reachEnd(now))
add(ans2, dp[0][j][s]);
else
add(dp[1][j + 1][now - pool], dp[0][j][s]);
}
}
}
memcpy(dp[0], dp[1], sizeof(dp[0]));
}
return make_pair(ans1, ans2);
}
int main()
{
int n, day;
while(scanf("%s", nowStr) == 1)
{
initialize();
scanf("%d%d", &day, &n);
for(int i = 0; i < n; i++)
{
scanf("%s", str);
insert(str);
}
build();
calDepth(root, 0);
pair<int, int > ans = DP(nowStr, day, n);
printf("%d %d\n", ans.first, ans.second);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: