POJ 2778 DNA序列
题意:给n条病毒DNA序列,然后问所有长度为L的DNA序列中,不包含病毒的序列有多少条。
分析:首先将病毒的DNA序列建AC自动机,然后建fail指针。将每个点编号作为状态,然后可以得每个点走一步后的状态,然后长度为L,相当于走了L步,矩阵快速幂即可。举个栗子
比如病毒是{AC,C}
图好难画啊,边就不连了。。
从0点,到0的方式有2个(G,T),到1的方式有1个(A),到2的方式有0个,到3的方式有1个(C)
从1点,到0的方式有2个(G,T),到1的方式有1个(A),到2的方式有1个,到3的方式有0个
从2点,到0的方式有2个(G,T),到1的方式有1个,到2的方式有0个,到3的方式有1个
从3点,到0的方式有2个(G,T),到1的方式有1个(A),到2的方式有0个,到3的方式有1个(C)
可以构造矩阵M
2,1,0,1
2,1,1,0
2,1,0,1
2,1,0,1
M[i][j]:表示从i状态到j状态的走一步的方式总数(就是我们上面列举的呀)
我们需要求长度为L的DNA序列有多少没有病毒,也就是从根开始走L步,问不经过病毒的路径条数
M矩阵表示走一步的状态,所以 ML 就表示走L步的所有情况,那么怎么确定没有走过病毒呢,只需要在构造矩阵的时候,将经过病毒的状态标为0,(也就是不走这条路)就行了
上面的矩阵去掉病毒后是
2,1,0,0
2,1,0,0
0,0,0,0
0,0,0,0
ps:开始用C++交T,然后用G++交,刚好1000ms…时限是1000ms….
然后看了一下优化,在矩阵乘法的时候,减少取模,也就是每一行取一次。时间就到188ms了..
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 5e5+10,mod=1e5;
int getid(char c)
{
if(c=='A') return 0;
else if(c=='C') return 1;
else if(c=='T') return 2;
else if(c=='G') return 3;
}
struct matrix
{
ll mat[105][105];
matrix()
{
mem(mat,0);
}
};
struct Trie
{
int next[maxn][5],fail[maxn],ed[maxn];
int root,L;
int newnode()
{
for(int i=0;i<4;i++)
next[L][i]=-1;
ed[L++]=0;
return L-1;
}
void init()
{
L=0;
root=newnode();
}
void sert(char buf[])
{
int len=strlen(buf);
int now=root;
for(int i=0;iint id=getid(buf[i]);
if(next[now][id]==-1)
next[now][id]=newnode();
now=next[now][id];
}
ed[now]=1;
}
void buildfail()
{
queue<int> Q;
fail[root]=root;
for(int i=0;i<4;i++)
if(next[root][i] == -1)
next[root][i]=root;//这里为什么要改变next?为了之后query的时候
else {
fail[next[root][i]]=root;
Q.push(next[root][i]);
}
while(!Q.empty())
{
int now=Q.front();
Q.pop();
if(ed[fail[now]]) ed[now]=1;
for(int i=0;i<4;i++){
if(ed[next[fail[now]][i]])
ed[next[now][i]]=1;
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(char *str)
{
int len=strlen(str);
int now=root;
int res=0;
for(int i=0;iint id=getid(str[i]),temp;
now=next[now][id],temp=now;
while(temp!=root)
{
res+=ed[temp];
ed[temp]=0;
temp=fail[temp];
}
}
return res;
}
matrix buildM()
{
matrix A;
for(int i=0;ifor(int j=0;j<4;j++)
if(!ed[i]&&!ed[next[i][j]])
{
A.mat[i][next[i][j]]++;
}
}
return A;
}
int getL()
{
return L;
}
};
int sz;
matrix mul(matrix A,matrix B)
{
matrix c;
for(int i=0;ifor(int j=0;jfor(int k=0;k*B.mat[k][j];
c.mat[i][j]=c.mat[i][j]%mod;
}
}
return c;
}
matrix qk(matrix A,ll n)
{
matrix B;
for(int i=0;i1;
while(n>0)
{
if(n&1) B=mul(A,B);
A=mul(A,A);
n=n/2;
}
return B;
}
char s1[15];
Trie ac;
int main()
{
int n,m;
ac.init();
scanf("%d %d",&n,&m);
for(int i=0;i"%s",s1);
ac.sert(s1);
}
ac.buildfail();
matrix A=ac.buildM();
sz=ac.getL();
A=qk(A,m);
ll ans=0;
for(int i=0;i0][i])%mod;
printf("%lld\n",ans);
return 0;
}
考研路茫茫——单词情结 HDU - 2243
题意:给一些词根(字符串),然后问包含这些词根的长度不超过L的单词有多少个
分析:这是一个求和的呀。长度为1,2,…L都要加起来。
因为不包含这些词根的数量好算点,根据上面的启示,我们可以将问题转化为求总单词数-不包含这些词根的单词总数
总单词数= 26+262+263+...+26L
不包含这些词根的单词数和上面那道题的求法一模一样。
不包含这些词根的单词总数也是一个求和,设构造矩阵为A,那么就是 A1+A2+A3+....+AL
这个求和怎么算比较快?就是构造下面这个矩阵,E为单位矩阵
A E
0 E
然后sum就为这个矩阵L次方后0行的上面两个矩阵的和-E,或者这个矩阵L+1次方后的右上角那个矩阵然后-E
对了,关于mod2e64,long long 的范围可以到2e63-1,unsigned long long 可以到2e64-1,直接用unsigned计算,就相当于取模啦
#define ll unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 105;
struct matrix
{
ll mat[105][105];
matrix()
{
mem(mat,0);
}
};
struct Trie
{
int next[maxn][30],fail[maxn],ed[maxn];
int root,L;
int newnode()
{
for(int i=0;i<26;i++)
next[L][i]=-1;
ed[L++]=0;
return L-1;
}
void init()
{
L=0;
root=newnode();
}
void sert(char buf[])
{
int len=strlen(buf);
int now=root;
for(int i=0;iint id=buf[i]-'a';
if(next[now][id]==-1)
next[now][id]=newnode();
now=next[now][id];
}
ed[now]=1;
}
void buildfail()
{
queue<int> Q;
fail[root]=root;
for(int i=0;i<26;i++)
if(next[root][i] == -1)
next[root][i]=root;//这里为什么要改变next?为了之后query的时候
else {
fail[next[root][i]]=root;
Q.push(next[root][i]);
}
while(!Q.empty())
{
int now=Q.front();
Q.pop();
if(ed[fail[now]]) ed[now]=1;
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 buildM()
{
matrix A;
for(int i=0;ifor(int j=0;j<26;j++)
if(!ed[next[i][j]])
{
A.mat[i][next[i][j]]++;
}
}
for(int i=0;i1;
for(int i=L;i*2;i++)
A.mat[i][i]=1;
return A;
}
int getL()
{
return 2*L;
}
};
matrix mul(matrix A,matrix B,int sz)
{
matrix c;
for(int i=0;ifor(int j=0;jfor(int k=0;k*B.mat[k][j];
}
}
return c;
}
matrix qk(matrix A,ll n,int sz)
{
matrix B;
for(int i=0;i1;
while(n>0)
{
if(n&1) B=mul(A,B,sz);
A=mul(A,A,sz);
n=n/2;
}
return B;
}
char s1[15];
Trie ac;
int main()
{
ll n,m;
while(cin>>n>>m){
ac.init();
for(int i=0;i"%s",s1);
ac.sert(s1);
}
ac.buildfail();
matrix A=ac.buildM();
int sz=ac.getL();
A=qk(A,m+1,sz);
ll res=0;
for(int i=sz/2;i0][i];
res--;
matrix B;
B.mat[0][0]=26;B.mat[0][1]=B.mat[1][1]=1;
B=qk(B,m,2);
ll ans=B.mat[0][0]+B.mat[0][1];
ans--;
ans=ans-res;
cout<return 0;
}