poj 2778 AC自动机+矩阵快速幂
題目鏈接:https://vjudge.net/problem/POJ-2778
題意:輸入n和m表示n個(gè)病毒,和一個(gè)長為m的字符串,里面只可以有'A','C','G','T' 這四個(gè)字符,現(xiàn)在問這個(gè)長為m的字符串里面不可以出現(xiàn)任何病毒的情況有多少。
參考的兩篇博客:
http://www.cnblogs.com/LQLlulu/p/9344774.html
https://blog.csdn.net/morgan_xww/article/details/7834801
上面的博客寫得很好,可以主要看上面的博客(兩個(gè)一起看)。
寫點(diǎn)東西,不一定對(duì)。
因?yàn)樽疃?0個(gè)病毒,每個(gè)病毒最多10個(gè)字符,所以我們trie樹上最多有100個(gè)點(diǎn),其他空的點(diǎn)都指向根節(jié)點(diǎn)(也就是說我們把所有的空的節(jié)點(diǎn)看成0),這樣我們可以到達(dá)的點(diǎn)就只有100個(gè)(再重復(fù)一遍:我們把trie樹上面的空節(jié)點(diǎn)一律指向根節(jié)點(diǎn),看成0),同時(shí)因?yàn)橛行c(diǎn)是單詞的結(jié)尾或者他的fail[u]是單詞結(jié)尾(fail[u]是單詞結(jié)尾說明當(dāng)前位置的后綴和fail[u]上的單詞相同,這個(gè)后綴也不能到達(dá))。
我們一開始先構(gòu)建一個(gè)cnt*cnt的鄰接矩陣mat(cnt是trie樹上面的節(jié)點(diǎn)個(gè)數(shù)),剛剛構(gòu)建的mat[i][j]代表從編號(hào)為i的點(diǎn)走一步到達(dá)編號(hào)為j的點(diǎn)的合法(就是不經(jīng)過單詞結(jié)尾或fail[u]是單詞結(jié)尾的點(diǎn)),在離散數(shù)學(xué)里面有一個(gè)結(jié)論,就是已知cnt個(gè)點(diǎn)之間兩兩互達(dá)(走一步)的可能數(shù)量(就是這個(gè)cnt*cnt的矩陣已經(jīng)有了),那么如果我們要計(jì)算他們兩兩之間走n步到達(dá)的可能數(shù)量,只需要求出矩陣的n次方,這個(gè)矩陣的n次方就是點(diǎn)與點(diǎn)之間走n步到達(dá)的可能數(shù)量,這個(gè)可以聯(lián)想矩陣乘法的計(jì)算過程。
代碼:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 105 int trie[maxn][4],fail[maxn],val[maxn]; LL mat[maxn][maxn],f[maxn][maxn],ss[maxn][maxn]; //mat[i][j]記錄從i到j(luò)有多少種可能,f[0][i]記錄以i結(jié)束的合法可能,其它維沒用,ss用來計(jì)算時(shí)暫時(shí)存儲(chǔ) int n,m,k,t,cnt; char s[12]; void init(){memset(trie,0,sizeof(trie));memset(fail,0,sizeof(fail));memset(val,0,sizeof(val));memset(mat,0,sizeof(mat));memset(f,0,sizeof(f));f[0][0]=1; //一開始把根節(jié)點(diǎn)賦值為1,因?yàn)槲覀冎恍枰谝恍?#xff0c;所以可以只把f[0][0]賦值為1/*for(int i=0;i<maxn;i++)f[i][i]=1; */ //也可以這樣 cnt=0; } int getID(char a){if(a=='A')return 0;if(a=='T')return 1;if(a=='C')return 2;if(a=='G')return 3; } void insert(char *s){int root=0;for(int i=0;s[i];i++){int id=getID(s[i]);if(trie[root][id]==0)trie[root][id]=++cnt;root=trie[root][id];} val[root]=1;//標(biāo)記單詞結(jié)尾 } void build_fail(){queue<int>q;int root=0;for(int i=0;i<4;i++){if(trie[root][i])q.push(trie[root][i]);}while(!q.empty()){int u=q.front();q.pop();if(val[fail[u]])//如果這個(gè)后綴是一個(gè)病毒,那么這個(gè)后綴也不可以到達(dá) val[u]=1;for(int i=0;i<4;i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else{trie[u][i]=trie[fail[u]][i];}}} } void build_mat(){//建圖 for(int i=0;i<=cnt;i++){for(int j=0;j<4;j++){if(val[trie[i][j]]==0&&val[i]==0)//當(dāng)前狀態(tài)或接下來的的狀態(tài)是病毒都是不可以到達(dá)的 mat[i][trie[i][j]]++;}} } void muti(LL a[][maxn],LL b[][maxn]){//矩陣快速冪 memset(ss,0,sizeof(ss));for(int k=0;k<=cnt;k++)for(int i=0;i<=cnt;i++){for(int j=0;j<=cnt;j++){ss[i][j]+=a[i][k]*b[k][j];ss[i][j]%=100000;}}for(int i=0;i<=cnt;i++){for(int j=0;j<=cnt;j++){a[i][j]=ss[i][j];}} } int main() {while(scanf("%d%d",&n,&m)!=EOF){init();for(int i=0;i<n;i++){scanf("%s",s);insert(s);}build_fail();build_mat();//獲得走1步時(shí)的矩陣 while(m){if(m&1)muti(f,mat);muti(mat,mat);m>>=1;}LL ans=0;for(int i=0;i<=cnt;i++)ans=(ans+f[0][i])%100000;printf("%lld\n",ans);}return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/6262369sss/p/10304410.html
總結(jié)
以上是生活随笔為你收集整理的poj 2778 AC自动机+矩阵快速幂的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 启动不了u盘安装系统怎么办 如何解决U盘
- 下一篇: 未来一瞥:机器人码农