http://poj.org/problem?id=2778
题意:
给你M个最多只有10个字符的字符串,问长度为N的不含这些字符串的字符串的
个数有多少个。N<=2000000000,M<=10
思路:
字符串匹配的问题,用AC自动机是最好的选择,因为N的范围很大, 直觉告诉
我们要用二分矩阵乘法。接着就是列状态转移方程,用F(i , j )表示 i 个字符
的字符串,最后一个串的状态为j时候的种数,状态转移方程就可以表示为:
F(i ,j )= sum{ F( i-1 , j' ) },其中j'到j一个合法的转移。这样我们就可以转
换为矩阵相乘。具体的注释见代码吧。
代码:
#include<stdio.h> #include<string.h> #include<queue> const __int64 Mod = 100000 ; int N ,M, Root ,cnt ; char ch[20] ; struct Node{ int fail ; //AC自动机的失败指针 bool danger ; //标记以该结点为结尾的字符串是否合法 int next[4] ; //next数组 void init(){ //初始化各个变量 fail = -1 ; danger = 0 ; memset(next , -1 ,sizeof(next)); } }p[110] ; std::queue<int> que ; __int64 g[110][110] ; //g[i][j]表示由状态i转移到状态j的种数 inline int get(char c){ switch(c){ case 'A' : return 0 ; case 'C' : return 1 ; case 'T' : return 2 ; case 'G' : return 3 ; default : return -1 ; } } void build_trie(char *ch){ int len = strlen(ch) ; int idx ,loc = Root; for(int i=0;i<len;i++){ idx = get(ch[i]) ; if( p[loc].next[idx] == -1 ){ ++cnt ; p[cnt].init() ; p[loc].next[idx] = cnt ; } loc = p[loc].next[idx] ; if( p[loc].danger ) return ; //如果当前字符串是前面某个字符串的前缀,则该字符串的接下去的状态都不合法 } p[loc].danger = 1 ; //最后的状态标记为不合法 } void build_ac_automation(){ int loc = Root ; p[loc].fail = -1 ; while(!que.empty()) que.pop() ; que.push(loc) ; while(!que.empty()){ int u = que.front() ; que.pop() ; for(int i=0;i<4;i++){ if( p[u].next[i] == -1 ) continue ; int v = p[u].next[i] ; if( u==Root ){ p[v].fail = Root ; } else{ int temp = p[u].fail ; while( temp!= -1 ){ if( p[temp].next[i] != -1){ p[v].fail = p[temp].next[i] ; if( p[ p[temp].next[i] ].danger ){ //和普通的建失败指针唯一不同的地方,请注意。 p[v].danger = 1 ; } break ; } temp = p[temp].fail ; } if( temp == -1 ) p[v].fail = Root ; } que.push(v) ; } } } void cal(){ memset(g, 0,sizeof(g)); for(int i=0;i<=cnt;i++){ if( p[i].danger ) continue; for(int j=0;j<4;j++){ if( p[i].next[j]!=-1 && p[ p[i].next[j] ].danger==0){ g[i][ p[i].next[j] ] ++ ; } else if( p[i].next[j] == -1 ){ int temp = p[i].fail ; while( temp != -1 ){ if( p[temp].next[j] != -1 ){ break ; } temp = p[temp].fail ; } if(temp == -1) g[i][Root] ++ ; else{ if( p[ p[temp].next[j] ].danger == 0 ) g[i][ p[temp].next[j] ] ++ ; } } } } } __int64 res[110][110] ; void calc(__int64 a[110][110],__int64 b[110][110]){ __int64 c[110][110] ; for(int i=0;i<=cnt;i++){ for(int j=0;j<=cnt;j++){ c[i][j] = 0 ; for(int k=0;k<=cnt;k++){ c[i][j] = ( c[i][j] + a[i][k] * b[k][j] % Mod ) % Mod ; } } } for(int i=0;i<=cnt;i++) for(int j=0;j<=cnt;j++) a[i][j] = c[i][j] ; } void solve(int k){ while(k){ if( k & 1 ){ calc(res, g); } calc(g,g) ; k >>= 1 ; } } int main(){ Root = 0 ; while(scanf("%d%d",&M,&N) == 2){ p[Root].init() ; cnt = 0 ; for(int i=1;i<=M;i++){ scanf("%s",ch); build_trie(ch) ; //构建字典树 } build_ac_automation() ; cal() ; memset(res,0,sizeof(res)); for(int i=0;i<110;i++) res[i][i]= 1 ; solve(N) ; __int64 ans = 0 ; for(int i=0;i<=cnt;i++){ ans += res[0][i] ; if( ans > Mod ) ans %= Mod ; } printf("%I64d\n",ans); } return 0 ; }