题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243
题意:中文题目,与POJ 2278比较相似,不多说了
大体思路:首先是对2^64取模这个问题,利用Hash算法中的思想,直接定义成unsigned long long,就可以了,在AC自动机的状态转移过程中,使用矩阵快速幂来进行优化加速,又因为是要求和,所以在构造的L*L矩阵中,额外增加一维,第L+1列全部为1,这样,我们就可以求出不包含模式串,且长度<=m的,然后总数剪掉这个就是答案了。
矩阵的加速:求A^1+A^2+A^3+...+A^l时可以加速不少
对于所有的AC自动机上的节点 i 枚举下一个可能的字符,即‘a’ - ‘z’,然后根据自动机的规则肯定会转移到某个节点 j ,如果 j 及 j 通过fail指针回指到root的路径上均无标记,那么mat[i][j] ++,表示i->j有多了一种方式或者一条路 。然后我们知道A[][] = mat[][]^K表示A[i][j]有多少种长度为K的走法。那么相反情况数为 sigma(mat[][]^i) (1 <= i <= n)。
AC代码:(此处参考bin巨模板)
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int idx(char c){return c - 'a';}
struct Matrix {
ull mat[30][30];
int n;
Matrix() {}
Matrix(int _n) {
n = _n;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j)
mat[i][j] = 0;
}
}
Matrix operator*(const Matrix &b) const { //重新定义矩阵乘法
Matrix ret = Matrix(n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k)
ret.mat[i][j] += mat[i][k] * b.mat[k][j];
}
}
return ret;
}
};
ull pow_mod(ull a, int n)
{
ull ret = 1, tmp = a;
while (n){
if(n & 1) ret *= tmp;
tmp *= tmp;
n >>= 1;
}
return ret;
}
Matrix pow_M(Matrix a, int n) //矩阵快速幂
{
Matrix ret = Matrix(a.n);
for(int i = 0; i < a.n; ++i)
ret.mat[i][i] = 1;
Matrix tmp = a;
while (n){
if(n & 1) ret = ret * tmp;
tmp = tmp * tmp;
n >>= 1;
}
return ret;
}
struct Trie
{
int next[35][26], fail[35];
bool last[35];
int root, L;
int newnode()
{
for(int i = 0; i < 26; ++i)
next[L][i] = -1;
last[L++] = false;
return L-1;
}
void init() {
L = 0;
root = newnode();
}
void insert(char word[])
{
int len = strlen(word);
int now = root;
for (int i = 0; i < len; ++i) {
int c = idx(word[i]);
if (next[now][c] == -1)
next[now][c] = newnode();
now = next[now][c];
}
last[now] = true;
}
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();
if(last[fail[now]]) last[now] = true;
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]);
}
}
}
}
Matrix get() //状态转移加速
{
Matrix ret = Matrix(L+1);
for (int i = 0; i < L; ++i) {
for (int j = 0; j < 26; ++j) {
if (last[next[i][j]] == false)
ret.mat[i][next[i][j]]++;
}
}
for (int i = 0; i < L+1; ++i) {
ret.mat[i][L] = 1;
}
return ret;
}
}trie;
char word[7];
int main()
{
int n, l;
while (~scanf("%d %d", &n, &l)){
trie.init();
for(int i = 0; i < n; ++i){
scanf("%s", word);
trie.insert(word);
}
trie.build();
Matrix a = trie.get();
a = pow_M(a, l);
ull res = 0;
for (int i = 0; i < a.n; ++i)
res += a.mat[0][i];
res--;
a = Matrix(2);
a.mat[0][0] = 26;
a.mat[1][0] = a.mat[1][1] = 1;
a = pow_M(a, l);
ull ans = a.mat[1][0] + a.mat[0][0]; //计算总数
ans--;
ans -= res;
cout << ans << '\n';
}
return 0;
}
这个代码已经很优化了,0ms,成功的排上了G++第5位,而前四位呢,鬼知道他们是用什么来写的,我感觉没有用到矩阵,他们的空间只用了不到400k,甚至只是200K,你敢信? 我是用了1424K....
POJ 2778 跟上面的那道题差不多,写法更为简单一点
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const int mod = 1e5;
char dna[20];
struct Matrix{
int mat[105][105], n;
Matrix(){}
Matrix(int _n)
{
n = _n;
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; j++)
mat[i][j] = 0;
}
}
Matrix operator *(const Matrix &b)const
{
Matrix ret = Matrix(n);
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
for(int k = 0; k < n; ++k){
int tmp = (ll)mat[i][k] * b.mat[k][j] % mod;
ret.mat[i][j] = (ret.mat[i][j] + tmp) % mod;
}
}
}
return ret;
}
};
struct Trie
{
int next[110][4], fail[110];
bool last[110];
int root, L;
int newnode()
{
for(int i = 0; i < 128; ++i) next[L][i] = -1;
last[L++] = false;
return L-1;
}
void init()
{
L = 0;
root = newnode();
}
int getch(char ch)
{
if(ch == 'A') return 0;
else if(ch == 'C') return 1;
else if(ch == 'G') return 2;
else if(ch == 'T') return 3;
}
void insert(char s[])
{
int len = strlen(s), now = root;
for(int i = 0; i < len; ++i){
if(next[now][getch(s[i])] == -1)
next[now][getch(s[i])] = newnode();
now = next[now][getch(s[i])];
}
last[now] = true;
}
void build()
{
queue<int> Q;
//fail[root] = root;
for(int i = 0; i < 4; ++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();
if(last[fail[now]] == true) last[now] = true;
for(int i = 0; i < 4; ++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]);
}
}
}
}
Matrix get()
{
Matrix res = Matrix(L);
for(int i = 0; i < L; ++i){
for(int j = 0; j < 4; ++j){
if(last[next[i][j]] == false)
res.mat[i][next[i][j]]++;
}
}
return res;
}
}trie;
Matrix pow_mod(Matrix a, int n)
{
Matrix ret = Matrix(a.n);
for(int i = 0; i < ret.n; ++i)
ret.mat[i][i] = 1;
Matrix tmp = a;
while (n){
if(n & 1) ret = ret * tmp;
tmp = tmp * tmp;
n >>= 1;
}
return ret;
}
int main()
{
int n, m;
while (~scanf("%d %d", &n, &m)){
trie.init();
for(int i = 0; i < n; ++i){
scanf("%s", dna);
trie.insert(dna);
}
trie.build();
Matrix a = trie.get();
a = pow_mod(a, m);
int ans = 0;
for(int i = 0; i < a.n; ++i)
ans = (ans + a.mat[0][i]) % mod;
cout << ans << '\n';
}
return 0;
}