POJ2778&HDU2243&POJ1625(AC自动机+矩阵/DP)
POJ2778
题意:只有四种字符的字符串(A, C, T and G),有M中字符串不能出现,为长度为n的字符串可以有多少种。
题解:在字符串上有L中状态,所以就有L*A(字符个数)中状态转移。这里自动机的build的hdu2222略有不同。
那一题中通过询问时循环来求she的he,但是如果he不能出现,she就算不是禁止的字符串也不可出现,所以在build的时候就记录所有不能出现的状态。
if (end[ fail[now] ]) end[now]++;
然后用一个矩阵F来记录可以相互到达的状态就OK了。
矩阵可以理解为用长度为1的字符串两个状态可以相互达到,而F=F^n,F[i][j]就是字符串长度为n时状态i到状态j的路径。
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> #include <set> using namespace std; const int N = 150; const int A = 4; const int M = 15; const int MOD = 100000; typedef vector<int> vec; typedef vector<vec> mat; mat mul(mat &A, mat &B) { mat C(A.size(), vec(B[0].size())); for (int i = 0; i < A.size(); ++i) { for (int k = 0; k < B.size(); ++k) { for (int j = 0; j < B[0].size(); ++j) { C[i][j] = (C[i][j] + (long long)A[i][k] * B[k][j]) % MOD; } } } return C; } // A^n mat pow(mat A, int n) { mat B(A.size(), vec(A.size())); for (int i = 0; i < A.size(); ++i) B[i][i] = 1; while (n > 0) { if (n & 1) B = mul(B, A); A = mul(A, A); n >>= 1; } return B; } struct ACAutomata { int next[N][A], fail[N], end[N]; int root, L; int idx(char ch) { switch(ch) { case 'A': return 0; case 'C': return 1; case 'T': return 2; case 'G': return 3; } return -1; } int newNode() { for (int i = 0; i < A; ++i) next[L][i] = -1; end[L] = 0; return L++; } void init() { L = 0; root = newNode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for (int i = 0; i < len; ++i) { int ch = idx(buf[i]); if (next[now][ch] == -1) next[now][ch] = newNode(); now = next[now][ch]; } end[now]++; } void build() { queue<int> Q; fail[root] = root; for (int i = 0; i < A; ++i) { if (next[root][i] == -1) { next[root][i] = root; } else { fail[ next[root][i] ] = root; Q.push( next[root][i] ); } } while (Q.size()) { int now = Q.front(); Q.pop(); if (end[ fail[now] ]) end[now]++; //!! for (int i = 0; i < A; ++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]); } } } } int query(int n) { mat F(L, vec(L)); for (int i = 0; i < L; ++i) { for (int j = 0; j < L; ++j) { F[i][j] = 0; } } for (int i = 0; i < L; ++i) { for (int j = 0; j < A; ++j) { int nt = next[i][j]; if (!end[nt]) F[i][nt]++; } } F = pow(F, n); int res = 0; for (int i = 0; i < L; ++i) { res = (res + F[0][i]) % MOD; } return res; } } ac; char buf[20]; int main() { int m, n; while (~scanf("%d%d", &m, &n)) { ac.init(); while (m--) { scanf("%s", buf); ac.insert(buf); } ac.build(); printf("%d\n", ac.query(n)); } return 0; }
HDU 2243上题要求不存在给定字符串,而这题要求存在至少一个。于是方法就是总方法减去不存在的就是答案了。
题目要求是小于等于L的,所以矩阵加一维记录前缀和就好了。
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ull; typedef vector<ull> vec; typedef vector<vec> mat; const int N = 150; const int A = 26; const int M = 15; mat mul(mat &A, mat &B) { mat C(A.size(), vec(B[0].size())); for (int i = 0; i < A.size(); ++i) { for (int k = 0; k < B.size(); ++k) { for (int j = 0; j < B[0].size(); ++j) { C[i][j] += A[i][k] * B[k][j]; } } } return C; } mat pow(mat A, ull n) { mat B(A.size(), vec(A.size())); for (int i = 0; i < A.size(); ++i) B[i][i] = 1; while (n > 0) { if (n & 1) B = mul(B, A); A = mul(A, A); n >>= 1; } return B; } struct ACAutomata { int next[N][A], fail[N], end[N]; int root, L; int idx(char ch) { return ch - 'a'; } int newNode() { for (int i = 0; i < A; ++i) next[L][i] = -1; end[L] = 0; return L++; } void init() { L = 0; root = newNode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for (int i = 0; i < len; ++i) { int ch = idx(buf[i]); if (next[now][ch] == -1) next[now][ch] = newNode(); now = next[now][ch]; } end[now]++; } void build() { queue<int> Q; fail[root] = root; for (int i = 0; i < A; ++i) { if (next[root][i] == -1) { next[root][i] = root; } else { fail[ next[root][i] ] = root; Q.push( next[root][i] ); } } while (Q.size()) { int now = Q.front(); Q.pop(); if (end[ fail[now] ]) end[now]++; for (int i = 0; i < A; ++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]); } } } } ull query(ull n) { mat F(L+1, vec(L+1)); for (int i = 0; i <= L; ++i) { for (int j = 0; j <= L; ++j) { F[i][j] = 0; } } for (int i = 0; i <= L; ++i) F[i][L] = 1; for (int i = 0; i < L; ++i) { for (int j = 0; j < A; ++j) { int nt = next[i][j]; if (!end[nt]) F[i][nt]++; } } F = pow(F, n+1); return F[0][L] - 1; } } ac; char aff[10]; int main() { int n; ull m; while (~scanf("%d%llu", &n, &m)) { ac.init(); while (n--) { scanf("%s", aff); ac.insert(aff); } ac.build(); ull ans1 = ac.query(m); mat F(2, vec(2)); mat S(2, vec(1)); S[0][0] = 26; S[1][0] = 0; F[0][0] = 26; F[0][1] = 0; F[1][0] = 1; F[1][1] = 1; F = pow(F, m); S = mul(F, S); ull ans2 = S[1][0]; printf("%llu\n", ans2 - ans1); } return 0; }
POJ1625
同poj2778,不过没有取模操作,需要大数。
大数不可以矩阵快速幂吗?内存不够……
使用dp递推,递推公式也比较简单。
#include <stdio.h> #include <string.h> #include <iostream> #include <queue> #include <map> using namespace std; typedef vector<int> vec; typedef vector<vec> mat; const int N = 110; const int A = 256; map<char, int> mp; struct BigInt { const static int mod = 10000; const static int DLEN = 4; int a[600],len; BigInt() { memset(a,0,sizeof(a)); len = 1; } BigInt(int v) { memset(a,0,sizeof(a)); len = 0; do { a[len++] = v%mod; v /= mod; }while(v); } BigInt(const char s[]) { memset(a,0,sizeof(a)); int L = strlen(s); len = L/DLEN; if(L%DLEN)len++; int index = 0; for(int i = L-1;i >= 0;i -= DLEN) { int t = 0; int k = i - DLEN + 1; if(k < 0)k = 0; for(int j = k;j <= i;j++) t = t*10 + s[j] - '0'; a[index++] = t; } } BigInt operator +(const BigInt &b)const { BigInt res; res.len = max(len,b.len); for(int i = 0;i <= res.len;i++) res.a[i] = 0; for(int i = 0;i < res.len;i++) { res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0); res.a[i+1] += res.a[i]/mod; res.a[i] %= mod; } if(res.a[res.len] > 0)res.len++; return res; } BigInt operator *(const BigInt &b)const { BigInt res; for(int i = 0; i < len;i++) { int up = 0; for(int j = 0;j < b.len;j++) { int temp = a[i]*b.a[j] + res.a[i+j] + up; res.a[i+j] = temp%mod; up = temp/mod; } if(up != 0) res.a[i + b.len] = up; } res.len = len + b.len; while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--; return res; } void output() { printf("%d",a[len-1]); for(int i = len-2;i >=0 ;i--) printf("%04d",a[i]); printf("\n"); } }; struct ACAutomata { int next[N][A], fail[N], end[N]; int root, L; int idx(char ch) { return mp[ch]; } int newNode() { for (int i = 0; i < A; ++i) next[L][i] = -1; end[L] = 0; return L++; } void init() { L = 0; root = newNode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for (int i = 0; i < len; ++i) { int ch = idx(buf[i]); if (next[now][ch] == -1) next[now][ch] = newNode(); now = next[now][ch]; } end[now]++; } void build() { queue<int> Q; fail[root] = root; for (int i = 0; i < A; ++i) { if (next[root][i] == -1) { next[root][i] = root; } else { fail[ next[root][i] ] = root; Q.push( next[root][i] ); } } while (Q.size()) { int now = Q.front(); Q.pop(); if (end[ fail[now] ]) end[now]++; //!! for (int i = 0; i < A; ++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]); } } } } mat query(int n) { mat F(L, vec(L)); for (int i = 0; i < L; ++i) { for (unsigned j = 0; j < mp.size(); ++j) { int nt = next[i][j]; if (!end[nt]) F[i][nt]++; } } return F; } } ac; char word[100]; BigInt dp[2][110]; int main() { int n, m, p; while (~scanf("%d%d%d", &n, &m, &p)) { ac.init(); getchar(); gets(word); mp.clear(); for (unsigned i = 0; i < strlen(word); ++i) mp[ word[i] ] = i; while (p--) { gets(word); ac.insert(word); } ac.build(); mat F = ac.query(m); int now = 0; dp[now][0] = 1; for (int i = 1; i < ac.L; ++i) dp[now][i] = 0; for (int i = 1; i <= m; ++i) { now ^= 1; for (int j = 0; j < ac.L; ++j) dp[now][j] = 0; for (int j = 0; j < ac.L; ++j) for (int k = 0; k < ac.L; ++k) if (F[j][k]) dp[now][k] = dp[now][k] + dp[now^1][j] * F[j][k]; } BigInt res = 0; for (int i = 0; i < ac.L; ++i) res = res + dp[now][i]; res.output(); } return 0; }