回想还是半个月前...跟着Matrix67的那文章做矩阵乘法....做到这题就卡住了...决心突破..这两周从Trie入门开始..到今天终于把这题给AC了...虽然这半个月做题量相比以前大大减少....但真正能初步掌握一种算法还是值得的...
首先这道题我是参考了几个解题报告的:
http://www.matrix67.com/blog/archives/276/
http://blog.csdn.net/dooder_daodao/article/details/6711186
如果需要具体思路请先看前面两篇..特别是后面一个题解很详细..并且代码很整洁...可以看出很多东西....
我就说下我做这题时出现的问题以及要注意的地方...
1、 ***A表示长度为4末位为A前三位任意但其不能是任意病毒的前缀..****代表长度为4每位都任意但其不能是任意病毒的前缀
2、 Built_AC_Automation时word标记需要逆向的传递...
3、 Built_AC_Automation时某个点没有哪个孩子不需要任何处理..而我开始是按POJ3691的方式处理的...
4、 Built_Matrix时问题很多...这个矩阵的意义是各个前缀相互到达方案数...表示每个前缀添加一个字符能转化到另一个前缀的方案数..
5、 Built_Matrix时...注意什么时候要对0点更新..也就是要找的点在其以及其当其不断向上Fail是都没有这个孩子...就要更新矩阵的0列...但如果这个过程中有某个点其孩子有但是其孩子是病毒点..那也不能更新矩阵的0列..应该不做任何操作..
6、 Built_Matrix时...若在其或者其向上Fail的某个点的孩子找到了能更新的点...那在++后就不要继续向上Fail了...因为继续往上在找到的实际上还是最先找到的那个..会重复的加..
7、 矩阵乘法时不能用递归...递归会爆内存..所以只能将其拆分成二阶乘数的和..
8 、切记!!矩阵里每个单元要是long long的...因为10^5 * 10^5会爆int !!
好久没写这么长的代码了...特别是非模拟题..怀疑这是我非模拟题写的最长的..比网络流的还长...PS: 我构造矩阵时就先将没用的点给去了..也就是病毒点..P就是有用点的从0到P.
Program:
#include<iostream> #include<stdio.h> #include<string.h> #include<queue> using namespace std; struct pp { long long s[105][105]; }h,_2M[32]; struct node { int s[4],Fail,w; bool word; }a[201]; int m,n,i,g,len,p,point[105]; long long ans; char s[15]; queue<int> myqueue; int turn(char c) { if (c=='A') return 0; if (c=='G') return 1; if (c=='C') return 2; return 3; } void Built_Trie(int h,int t) { int k=turn(s[t]); if (t==len) { a[h].word=true; return; } if (!a[h].s[k]) { g++; a[h].s[k]=g; } Built_Trie(a[h].s[k],t+1); } int UpdateFail(int h,int k) { if (a[h].s[k]) return a[h].s[k]; else if (!h) return h; else return UpdateFail(a[h].Fail,k); } void Built_AC_Automation() { int h,i; while (!myqueue.empty()) myqueue.pop(); myqueue.push(0); while (!myqueue.empty()) { h=myqueue.front(); myqueue.pop(); for (i=0;i<4;i++) if (a[h].s[i]) { if (!h) a[a[h].s[i]].Fail=h; else a[a[h].s[i]].Fail=UpdateFail(a[h].Fail,i); myqueue.push(a[h].s[i]); if (a[a[a[h].s[i]].Fail].word) a[a[h].s[i]].word=true; // 将病毒传递下去... } } return; } void Built_Matrix() { int k,i,kk; bool f; for (k=0;k<=p;k++) // 扫描所有构造矩阵时的有效点 { for (i=0;i<4;i++) // 扫描孩子 { kk=point[k]; f=true; while (1) // kk为头结点也要进去做..但做完后就没必要再调用自己了 { if (a[kk].s[i]) { f=false; // 如果kk在不断的Fail中找到了一个点孩子有i..显然后面就不需要更新h.s[k][0]了..标记 if (!a[a[kk].s[i]].word) // 如果其孩子这个点不为病毒..那么更新 { h.s[k][a[a[kk].s[i]].w]++; goto A; } else goto A; // 总之进来了就要跳出了...找到了不要继续Fail了..找到了是个病毒也不能继续Fail了.. } if (!kk) break; // 所以在这里判断跳出循环 kk=a[kk].Fail; } if (f) h.s[k][0]++; A: ; } } } pp Mul(pp a,pp b) { pp h; int i,j,k; memset(h.s,0,sizeof(h.s)); for (k=0;k<=p;k++) for (i=0;i<=p;i++) for (j=0;j<=p;j++) h.s[i][j]=(h.s[i][j]+a.s[i][k]*b.s[k][j])%100000; return h; } pp GetAnswer(int n) { int i=1,j,m; pp ans; _2M[i]=h; m=1; while (i<=30) { i++; _2M[i]=Mul(_2M[i-1],_2M[i-1]); m*=2; } // 得到每个2^K矩阵的值..因为N的范围小于2^31所以做30个足矣 memset(ans.s,0,sizeof(ans.s)); for (j=0;j<=p;j++) ans.s[j][j]=1; while (n) { while (m>n) { m/=2; i--; } ans=Mul(ans,_2M[i]); n-=m; } return ans; } int main() { scanf("%d%d",&m,&n); getchar(); g=0; memset(a,0,sizeof(a)); while (m--) { scanf("%s",s); len=strlen(s); Built_Trie(0,0); } Built_AC_Automation(); //------ 去掉病毒点..记录构造矩阵时的有效点.. p=0; point[0]=0; a[0].w=0; for (i=1;i<=g;i++) if (!a[i].word) { p++; point[p]=i; a[i].w=p; } //------ 去掉病毒点..记录构造矩阵时的有效点.. memset(h.s,0,sizeof(h.s)); Built_Matrix(); h=GetAnswer(n); ans=0; for (i=0;i<=p;i++) ans+=h.s[0][i]; printf("%I64d\n",ans%100000); return 0; }