日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

poj 2778 AC自动机+矩阵快速幂

發(fā)布時間:2024/10/12 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 poj 2778 AC自动机+矩阵快速幂 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

題目鏈接:https://vjudge.net/problem/POJ-2778

題意:輸入n和m表示n個病毒,和一個長為m的字符串,里面只可以有'A','C','G','T' 這四個字符,現(xiàn)在問這個長為m的字符串里面不可以出現(xiàn)任何病毒的情況有多少。

參考的兩篇博客:

http://www.cnblogs.com/LQLlulu/p/9344774.html

https://blog.csdn.net/morgan_xww/article/details/7834801

上面的博客寫得很好,可以主要看上面的博客(兩個一起看)。

寫點(diǎn)東西,不一定對。

因?yàn)樽疃?0個病毒,每個病毒最多10個字符,所以我們trie樹上最多有100個點(diǎn),其他空的點(diǎn)都指向根節(jié)點(diǎn)(也就是說我們把所有的空的節(jié)點(diǎn)看成0),這樣我們可以到達(dá)的點(diǎn)就只有100個(再重復(fù)一遍:我們把trie樹上面的空節(jié)點(diǎn)一律指向根節(jié)點(diǎn),看成0),同時因?yàn)橛行c(diǎn)是單詞的結(jié)尾或者他的fail[u]是單詞結(jié)尾(fail[u]是單詞結(jié)尾說明當(dāng)前位置的后綴和fail[u]上的單詞相同,這個后綴也不能到達(dá))。

我們一開始先構(gòu)建一個cnt*cnt的鄰接矩陣mat(cnt是trie樹上面的節(jié)點(diǎn)個數(shù)),剛剛構(gòu)建的mat[i][j]代表從編號為i的點(diǎn)走一步到達(dá)編號為j的點(diǎn)的合法(就是不經(jīng)過單詞結(jié)尾或fail[u]是單詞結(jié)尾的點(diǎn)),在離散數(shù)學(xué)里面有一個結(jié)論,就是已知cnt個點(diǎn)之間兩兩互達(dá)(走一步)的可能數(shù)量(就是這個cnt*cnt的矩陣已經(jīng)有了),那么如果我們要計(jì)算他們兩兩之間走n步到達(dá)的可能數(shù)量,只需要求出矩陣的n次方,這個矩陣的n次方就是點(diǎn)與點(diǎn)之間走n步到達(dá)的可能數(shù)量,這個可以聯(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ì)算時暫時存儲 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]])//如果這個后綴是一個病毒,那么這個后綴也不可以到達(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步時的矩陣 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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。