【AC自動機+矩陣快速冪】POJ - 2778 - DNA Sequence & HDU - 2243 - 考研路茫茫——單詞情結
阿新 • • 發佈:2018-11-10
POJ - 2778 - DNA Sequence
題目連結<http://poj.org/problem?id=2778>
題意:
DNA序列只包含ACTG四個字元,已知一些病毒的DNA序列,問你序列長度為n(1 <= n <=2000000000)且不包含病毒的數量。
題解:
對於一張有向圖,恰好走K步(可以走重邊)的方案數就是求鄰接矩陣的k次冪:
- 表示從i到j走1步的走法。
- 做一次乘法:,也就是以k為一次中轉,表示從i到j走2步的走法。
- 那麼就表示從i到j走k步的走法。
AC自動機本身就是一張有向圖,可以作為狀態進行轉移。每個點都有四個方向(加上四個符號),如果沒有沒有包含病毒那麼就是可以走的。建立fail指標的時候可以順便把所有地方的終止符號都打上。
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> #include <map> using namespace std; typedef long long ll; const ll N=1e2+7; const ll mod=100000; ll tot; map<char,ll>mp; struct Mat{ ll mat[105][105]; }a; Mat operator *(Mat a,Mat b){ Mat c; memset(c.mat,0,sizeof(c.mat)); for(ll k=0;k<tot;k++){ for(ll i=0;i<tot;i++){ if(a.mat[i][k]==0) continue; for(ll j=0;j<tot;j++){ if(b.mat[k][j]==0) continue; c.mat[i][j]=(c.mat[i][j]+(a.mat[i][k]*b.mat[k][j])%mod)%mod; } } } return c; } Mat operator ^(Mat a,ll b){ Mat c; for(ll i=0;i<tot;i++) for(ll j=0;j<tot;j++) c.mat[i][j]=(i==j); for(ll i=b;i;i>>=1,a=a*a) if(i&1) c=c*a; return c; } struct Trie { ll nxt[N][26],fail[N],ed[N]; ll rt; ll newnode(){ for(ll i=0;i<4;i++) nxt[tot][i]=-1; ed[tot++]=0; return tot-1; } void init(){ tot=0; rt=newnode(); } void ist(char buf[]){ ll len=strlen(buf); ll now=rt; for(ll i=0;i<len;i++){ ll vi=mp[buf[i]]; if(nxt[now][vi]==-1) nxt[now][vi]=newnode(); now=nxt[now][vi]; } ed[now]++; } void build(){ queue<ll>q; fail[rt]=rt; for(ll i=0;i<4;i++){ if(nxt[rt][i]==-1) nxt[rt][i]=rt; else{ fail[nxt[rt][i]]=rt; q.push(nxt[rt][i]); } } while(!q.empty()){ ll now=q.front(); if(ed[fail[now]]) ed[now]=1; q.pop(); for(ll i=0;i<4;i++){ if(nxt[now][i]==-1) nxt[now][i]=nxt[fail[now]][i]; else{ fail[nxt[now][i]]=nxt[fail[now]][i]; q.push(nxt[now][i]); } } } } void bmat(){ memset(a.mat,0,sizeof(a.mat)); for(ll u=0;u<tot;u++){ if(ed[u]) continue; for(ll i=0;i<4;i++){ ll v=nxt[u][i]; if(ed[v]==0) a.mat[u][v]++; } } } }ac; char buf[N]; int main() { ll n,m; ac.init(); mp['A']=0;mp['C']=1; mp['T']=2;mp['G']=3; scanf("%lld%lld",&n,&m); for(ll i=0;i<n;i++){ scanf("%s",buf); ac.ist(buf); } ac.build(); ac.bmat(); a=a^m; ll ans=0; for(ll i=0;i<tot;i++) ans=(ans+a.mat[0][i])%mod; printf("%lld\n",ans); return 0; }
HDU - 2243 - 考研路茫茫——單詞情結
題目連結<http://acm.hdu.edu.cn/showproblem.php?pid=2243>
題意:
與上一題很類似。
給出若干個字串詞根,求出長度不超過L,且至少包括一個詞根的字串個數。
題解:
做法也和上一題基本一樣,這題需要多加一個求字首和的運算,只需要修改一下快速冪就行了。
總數是,減去求出來的不包含詞根的個數即可。
另外這題要用unsigned long long,並且自帶取模。
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> #include <map> using namespace std; typedef unsigned long long ll; const ll N=1e2+7; ll tot; struct Mat{ ll mat[105][105]; }a; Mat operator *(Mat a,Mat b){ Mat c; memset(c.mat,0,sizeof(c.mat)); for(ll k=0;k<tot;k++){ for(ll i=0;i<tot;i++){ if(a.mat[i][k]==0) continue; for(ll j=0;j<tot;j++){ if(b.mat[k][j]==0) continue; c.mat[i][j]=(c.mat[i][j]+(a.mat[i][k]*b.mat[k][j])); } } } return c; } Mat operator +(Mat a,Mat b){ Mat c; for(ll i=0;i<tot;i++) for(ll j=0;j<tot;j++) c.mat[i][j]=(a.mat[i][j]+b.mat[i][j]); return c; } Mat operator ^(Mat a,ll b){ Mat c,t=a; memset(c.mat,0,sizeof(c.mat)); for(ll i=b;i;i>>=1,a=a*a){ if(i&1) c=t+c*a; t=t+t*a; } return c; } struct Trie { ll nxt[N][26],fail[N],ed[N]; ll rt; ll newnode(){ for(ll i=0;i<26;i++) nxt[tot][i]=-1; ed[tot++]=0; return tot-1; } void init(){ tot=0; rt=newnode(); } void ist(char buf[]){ ll len=strlen(buf); ll now=rt; for(ll i=0;i<len;i++){ ll vi=buf[i]-'a'; if(nxt[now][vi]==-1) nxt[now][vi]=newnode(); now=nxt[now][vi]; } ed[now]++; } void build(){ queue<ll>q; fail[rt]=rt; for(ll i=0;i<26;i++){ if(nxt[rt][i]==-1) nxt[rt][i]=rt; else{ fail[nxt[rt][i]]=rt; q.push(nxt[rt][i]); } } while(!q.empty()){ ll now=q.front(); if(ed[fail[now]]) ed[now]=1; q.pop(); for(ll i=0;i<26;i++){ if(nxt[now][i]==-1) nxt[now][i]=nxt[fail[now]][i]; else{ fail[nxt[now][i]]=nxt[fail[now]][i]; q.push(nxt[now][i]); } } } } void bmat(){ memset(a.mat,0,sizeof(a.mat)); for(ll u=0;u<tot;u++){ if(ed[u]) continue; for(ll i=0;i<26;i++){ ll v=nxt[u][i]; if(ed[v]==0) a.mat[u][v]++; } } } }ac; char buf[N]; ll qpow(ll a,ll b){ ll ans=0; ll t=a; for(ll i=b;i;i>>=1,a=a*a){ if(i&1) ans=t+ans*a; t=t+t*a; } return ans; } int main() { ll n,m; while(scanf("%llu%llu",&n,&m)!=EOF){ ac.init(); for(ll i=0;i<n;i++){ scanf("%s",buf); ac.ist(buf); } ac.build(); ac.bmat(); a=a^m; ll ans=qpow(26,m); for(ll i=0;i<tot;i++) ans=ans-a.mat[0][i]; printf("%llu\n",ans); } return 0; }