タイトルリンク
神の称号。
acオートマトンの以前のスタディノートで、acオートマトンの本質は状態遷移グラフを作成することであり、パターンマッチングのプロセスは実際にはグラフ上で状態遷移を実行することであると述べました。長さnの文字列を作成することは、実際には、状態遷移グラフのルートノードからnステップを実行してすべてのスキームの数を見つけることと同じです。この質問では、対象の文字列に表示できない文字列がいくつかあります。つまり、状態遷移グラフで対応する文字列の終点が位置するノードに到達できない、つまり到達できません。
nが非常に小さい場合、dpを使用してこの問題を解決できます。dp [i] [j]は、現在の文字列の長さがi、状態遷移グラフのノードがj、遷移方程式がdp [i + 1] [ch [j] [k]] + = dp [i] [であることを示します。 j]。しかし、この質問nは大きすぎます。明らかに、dp配列は明らかに開かれていないため、ここでは、状態遷移を行列で表現し、状態遷移行列を構築し、行列の高速なパワーによって遷移プロセスを高速化することを考えます。これは、数ステップ後に数回移動し、nステップで遷移行列のn乗を見つけることとして理解できます。
行列要素はllとtを開きます。64ビット整数に対するPojのサポートはあまりにも悪いと推測されます。
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
#define fi first
#define se second
#define FIN freopen("in.txt","r",stdin)
#define FIO freopen("out.txt","w",stdout)
#define INF 0x3f3f3f3f
#define per(i,a,n) for(int i = a;i < n;i++)
#define rep(i,a,n) for(int i = n;i > a;i--)
#define pern(i,a,n) for(int i = a;i <= n;i++)
#define repn(i,a,n) for(int i = n;i >= a;i--)
#define fastio std::ios::sync_with_stdio(false)
#define all(a) a.begin(), a.end()
#define ll long long
#define pb push_back
#define endl "\n"
#define pii pair<int,int>
#define sc(n) scanf("%d", &n)
#define CASET int ___T; scanf("%d", &___T); for(int cs=1;cs<=___T;cs++)
template<typename T> inline void _max(T &a,const T b){
if(a<b) a = b;}
template<typename T> inline void _min(T &a,const T b){
if(a>b) a = b;}
using namespace std;
//inline ll read(){
// ll a=0;int f=0;char p=getchar();
// while(!isdigit(p)){f|=p=='-';p=getchar();}
// while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
// return f?-a:a;
//}
const int maxn = 10000*50;
const int mod = 100000;
const int Size = 110;
struct Marix{
int a[Size][Size];
int n;
Marix(){
n = Size;
memset(a, 0, sizeof(a));
}
Marix(int _n)
{
n = _n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
a[i][j] = 0;
}
}
Marix operator * (const Marix &A){
Marix B = Marix(A.n);
for(int k = 0; k < A.n; ++k){
for(int i = 0; i < A.n; ++i){
for(int j = 0; j < A.n; ++j){
B.a[i][j] = (B.a[i][j] + (a[i][k]*A.a[k][j] % (mod)) ) % (mod);
}
}
}
return B;
}
};
Marix mul(Marix a,Marix b){
//矩阵乘法
Marix res=Marix(a.n);
for(int i=0;i<a.n;i++){
for(int j=0;j<a.n;j++){
for(int k=0;k<a.n;k++){
int tmp=(long long )a.a[i][k]*b.a[k][j]%mod;
res.a[i][j]=(res.a[i][j]+tmp)%mod;
}
}
}
return res;
}
Marix powMod(Marix A, ll cnt){
Marix res = Marix(A.n);
for(int i = 0; i < A.n; ++i)res.a[i][i] = 1;
while(cnt){
if(cnt&1)res = mul(res,A);
//cout << res.a[0][1] << endl;
A = mul(A,A);
cnt >>= 1;
}
return res;
}
const int maxnode = 4;
int ch[maxn][maxnode]; //字典树
int cnt[maxn]; //单词出现次数
int sz;
int fail[maxn];
map<char,int> mp;
void init()
{
mp['A'] = 0,mp['C']=1,mp['T']=2,mp['G']=3;
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
//val[0] = 0;
cnt[0] = 0;
}
void insert(char str[], int len) //插入字符串
{
int u = 0;
per(i, 0, len)
{
int v = mp[str[i]];
if (!ch[u][v])
{
memset(ch[sz], 0, sizeof(ch[sz]));
//val[sz] = 0;
cnt[sz] = 0;
ch[u][v] = sz++;
}
u = ch[u][v];
}
cnt[u]=1;
//在这里我们可以建立一个int-string的映射,以通过节点序号得知这个点是哪个单词的结尾
}
void getfail()
{
//所有模式串已插入完成
queue<int> q;
per(i, 0, maxnode)
{
if (ch[0][i])
{
fail[ch[0][i]] = 0;
q.push(ch[0][i]);
}
}
while (!q.empty())
{
int now = q.front();
q.pop();
per(i, 0, maxnode)
{
if (ch[now][i])
{
fail[ch[now][i]] = ch[fail[now]][i];
q.push(ch[now][i]);
}
else
ch[now][i] = ch[fail[now]][i];
}
cnt[now] |= cnt[fail[now]];
}
}
int query(char str[], int len)
{
int now = 0, ans = 0;
per(i, 0, len)
{
now = ch[now][str[i] - 'a'];
int j = now;
while (j && cnt[j] != -1)
{
ans += cnt[j];
cnt[j] = -1; //防止重复计算。这里可以将j映射到string,以具体统计每个模式串出现的次数。j即节点序号,模式串与其是一一对应的关系
j = fail[j];
}
}
return ans;
}
Marix getmatrix()
{
Marix ma = Marix(sz);
per(i,0,sz)
{
per(j,0,4)
{
if(cnt[ch[i][j]]==1)continue;
ma.a[i][ch[i][j]]++;
}
}
return ma;
}
char s[20];
int main()
{
// #ifndef ONLINE_JUDGE
// int startTime = clock();
//FIN;
// #endif
//fastio;
//忘记初始化是小狗
//freopen("out.txt","w",stdout);
//ios::sync_with_stdio(false);
int n,m;
//cout << id('0');
sc(n);
sc(m);
init();
per(i,0,n)
{
scanf("%s",s);
insert(s,strlen(s));
}
getfail();
Marix mm = getmatrix();
// per(i,0,sz)
// {
// per(j,0,sz)cout << mm.a[i][j] << ' ';
// cout << endl;
// }
Marix res = powMod(mm,m);
ll ans = 0;
per(i,0,sz)ans = (ans+res.a[0][i])%mod;
cout << ans << endl;
// #ifndef ONLINE_JUDGE
// printf("\nTime = %dms\n", clock() - startTime);
// #endif
return 0;
}