您的位置:首页 > 产品设计 > UI/UE

poj -- 2778 DNA Sequence && hdu -- 2243 考研路茫茫——单词情结(AC自动机 + 矩阵)

2014-08-12 09:36 543 查看
这两道题都是需要用矩阵加速的AC自动机。

poj -- 2778 DNA Sequence

这道题是说,给一些DNA序列,求长度为n的序列有多少是完全不含这些序列的。

(在AC自动机中,我用cnt数组来表示该节点是否为一个序列的尾节点。。)

首先,我们将个的m个序列加到AC自动机中。我们如何做才能使构造的串不含任意一个trie树中的字符串呢?

只要不遍历所有字符串的尾节点就行!

我们利用这个构建矩阵,矩阵中mat[i][j]就是从i到j的路径条数(长度是1的),然后我们求这个矩阵的n次幂就是长度为n的路径条数。

//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 100000;
const int maxn = 110 + 10;

struct Matrix
{
int mat[110][110],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 AC_automata{
int chd[maxn][4], fail[maxn], cnt[maxn];///trie中的chd数组,fail指针,节点标记(是多少个串的结尾)
int root, sz;///根节点,trie树大小
int newnode(){///建立新的节点,返回节点编号
for(int i=0; i<4; i++) chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int id(char ch){
if(ch == 'A') return 0;
if(ch == 'T') return 1;
if(ch == 'G') return 2;
if(ch == 'C') return 3;
}
void Insert(char * str){///建trie树
int cur = root;
for(int i=0; str[i]; i++){
if(chd[cur][id(str[i])] == -1)
chd[cur][id(str[i])] = newnode();
cur = chd[cur][id(str[i])];
}
cnt[cur] = 1;
}
void build(){///构建fail指针
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<4; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else{
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
if(cnt[fail[now]] == 1) cnt[now] = 1;///一个节点的fail指针指向的节点如果是一个串的尾节点,那么这个节点一定也是相同串的尾节点
for(int i=0; i<4; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else {
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
Matrix getMatrix(){
Matrix res = Matrix(sz);
for(int i=0;i<sz;i++)
for(int j=0;j<4;j++)
if(cnt[chd[i][j]] == 0)///若为1则从i不能走到chd[i][j],否则串中就会含有trie树中的串
res.mat[i][chd[i][j]]++;///i到chd[i][j]的路径条数
return res;
}

}AC;
Matrix pow_M(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 m, n;
char str[50];
int main(){
while(scanf("%d%d", &m, &n) == 2){
AC.init();
for(int i=1; i<=m; i++){
scanf("%s", str);
AC.Insert(str);
}
AC.build();
Matrix A = AC.getMatrix();
A = pow_M(A, n);///变成长度为n的路径
int ans = 0;
for(int i=0; i<A.n; i++)
ans = (ans + A.mat[0][i]) % mod;
printf("%d\n", ans);
}
return 0;
}

hdu -- 2243 考研路茫茫——单词情结

这道题和上面的相类似。

给n个字符串,求长度不超过L的字符串中有多少至少包含一个给定的串。

想要求出这个问题,我们只需求出    所有可能的字符串 - 所有不包含指定串的字符串。

所有的字符串是26^1 + 26^2 + .... + 26^L。

求不包含指定串的字符串就和上题一样了。这里需要给矩阵加一维,用来求出长度为1~L的不包含指定串的字符串之和。

//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;

#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 100000;
const int maxn = 110 + 10;

struct Matrix
{
ull mat[110][110];
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++){
ull tmp=mat[i][k]*b.mat[k][j];
ret.mat[i][j]=ret.mat[i][j]+tmp;
}
return ret;
}
};
struct AC_automata{
int chd[maxn][30], fail[maxn], cnt[maxn];///trie中的chd数组,fail指针,节点标记(是多少个串的结尾)
int root, sz;///根节点,trie树大小
int newnode(){///建立新的节点,返回节点编号
for(int i=0; i<26; i++) chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int id(char ch){
return ch - 'a';
}
void Insert(char * str){///建trie树
int cur = root;
for(int i=0; str[i]; i++){
if(chd[cur][id(str[i])] == -1)
chd[cur][id(str[i])] = newnode();
cur = chd[cur][id(str[i])];
}
cnt[cur] = 1;
}
void build(){///构建fail指针
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<26; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else{
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
if(cnt[fail[now]] == 1) cnt[now] = 1;///一个节点的fail指针指向的节点如果是一个串的尾节点,那么这个节点一定也是相同串的尾节点
for(int i=0; i<26; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else {
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
Matrix getMatrix(){
Matrix res = Matrix(sz + 1);
for(int i=0;i<sz;i++)
for(int j=0;j<26;j++)
if(cnt[chd[i][j]] == 0)///若为1则从i不能走到chd[i][j],否则串中就会含有trie树中的串
res.mat[i][chd[i][j]]++;///i到chd[i][j]的路径条数
for(int i=0; i<=sz; i++)
res.mat[i][sz] = 1;
//        for(int i=0; i<=sz; i++)
//        for(int j=0; j<=sz; j++){
//                cout << res.mat[i][j] ;
//                printf("%c", j == sz ? '\n' : ' ');
//        }
return res;
}

}AC;
Matrix pow_M(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 m, n;
char str[50];
int main(){
while(scanf("%d%d", &m, &n) == 2){
AC.init();
for(int i=1; i<=m; i++){
scanf("%s", str);
AC.Insert(str);
}
AC.build();
Matrix A = AC.getMatrix();
A = pow_M(A, n);///变成长度为n的路径
ull ans = 0;
for(int i=0; i<A.n; i++)
ans += A.mat[0][i];
A = Matrix(2);
A.mat[0][0] = 26;
A.mat[1][0] = A.mat[1][1] = 1;
A = pow_M(A, n);
//        cout << ans << endl;
ans = A.mat[0][0] + A.mat[1][0] - ans;///总共的减掉不含的就是答案
cout << ans << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  矩阵 AC自动机