暑假剛學完AC自動機模板的時候,看見這道題就感覺這題好難,經過幾次複習與學習,突然發現這題不是那麼的難。
題意:給你10個只由‘A’、‘T’、‘C’、‘G’這四個字母組成的最大長度爲10的字符串。問有多少個不同的長度爲n (1 <= n <=2000000000).的字符串。結果對100000取餘。
解法:之前做的都是狀壓DP,見到這題也想這麼做,突然發現長度略大,DP一定TLE。由於數據量是10^9,本能反應就是log(n)算法,看那衆多大神的題解,學習到,原來矩陣也可以這麼用。先是做一個AC自動機,然後做狀態轉移矩陣:枚舉每一個節點,然後讓這個節點添加一個字母(A、T、C、G)判斷是不是單詞節點,如果不是就把矩陣的對應位置++;如果是單詞節點,那麼就尋找適配路徑之前的節點,同上處理。最後就能得到一個“節點數*節點數”大小的矩陣,每個位置代表從某個狀態轉移到另一個狀態的方案數。最後求矩陣的n次方,需要矩陣加速,也就是快速冪的思路。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define pi acos(-1.0)
#define eps 1e-8
using namespace std;
const int maxnode = 110;
const int charsize = 4;
const int mod = 100000;
int ch[maxnode][charsize], f[maxnode], val[maxnode], last[maxnode];
int sz, n, m;
void init() {memset(ch[0],0,sizeof ch[0]);sz=1;}
int idx(char c) {if(c=='A') return 0; if(c=='T') return 1; if(c=='C') return 2; return 3;}
void insert(char *s, int v = 1)
{
int u = 0, n = strlen(s);
for(int i = 0 ; i < n ; ++ i)
{
int c = idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz],0,sizeof ch[sz]);
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v;
}
void getfail()
{
queue<int> q;
f[0] = 0;
for(int c = 0 ; c < charsize ; ++ c)
{
int u = ch[0][c];
if(u) {f[u]=last[u]=0;q.push(u);}
}
while(!q.empty())
{
int r = q.front(); q.pop();
for(int c = 0 ; c < charsize ; ++ c)
{
int u = ch[r][c];
if(!u) { ch[r][c] = ch[f[r]][c];continue;}
q.push(u);
int v = f[r];
while(v&&!ch[v][c]) v = f[v];
f[u] = ch[v][c];
last[u] = val[f[u]]?f[u]:last[f[u]];
}
}
}
struct mat
{
long long m[maxnode][maxnode];
mat operator*(const mat& ma) const
{
mat c;
memset(c.m, 0, sizeof c.m);
for(int i = 0 ; i < sz ; ++ i)
{
for(int j = 0 ; j < sz ; ++ j)
{
for(int k = 0 ; k < sz ; ++ k)
{
c.m[i][j] += m[i][k]*ma.m[k][j];
}
if(c.m[i][j]>=mod) c.m[i][j]%=mod;
}
}
return c;
}
}g;
mat pow(mat m, int n)
{
mat tmp;
for(int i = 0 ; i < sz ; ++ i)
{
for(int j = 0 ; j < sz ; ++ j)
{
tmp.m[i][j] = (i==j);
}
}
while(n)
{
if(n&1) tmp = tmp*m;
m = m*m;
n>>=1;
}
return tmp;
}
int main()
{
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&m))
{
char s[maxnode];
init();
for(int i = 0 ; i < n ; ++ i)
{
scanf("%s",s);
insert(s);
}
getfail();
for(int i = 0 ; i < sz ; ++ i)
{
if(val[i]||last[i]) continue;
for(int c = 0 ; c < charsize ; ++ c)
{
int u = ch[i][c];
if(u)
{
if(!val[u]&&!last[u]) g.m[i][u] ++;
}
else
{
int t = f[i];
while(t)
{
int u = ch[t][c];
if(u&&!val[u]&&!last[u])
{
g.m[i][u]++;
break;
}
t = f[t];
}
if(t==0)
{
int u = ch[t][c];
if(!u&&!val[u]&&!last[u]) g.m[i][u]++;
else if(!u) g.m[i][0]++;
}
}
}
}
mat tt = pow(g, m);
long long ans = 0;
for(int i = 0 ; i < sz ; ++ i)
{
ans += tt.m[0][i];
ans%=mod;
}
printf("%I64d\n",ans);
}
return 0;
}