模板题:
/***********************************\
* @prob: hdu2222 Keywords Search *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: June. 23rd, 2012 *
* @memo: AC自动机 *
\***********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
const int maxN = 1000010;
struct Trie
{
int cnt; Trie* Fail; Trie* next[26];
Trie(): cnt(0), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
} *root;
char str[maxN]; int n, T;
inline void Ins(const char* str)
{
Trie* p = root;
while (*str)
{
int Index = *str++ - 'a';
if (!p -> next[Index])
p -> next[Index] = new Trie();
p = p -> next[Index];
} /* while */
++p -> cnt; return;
} /* Ins */
inline void Build()
{
static const int SIZE = 0xfffff;
static Trie* q[SIZE + 1];
static Trie* Now;
static Trie* p;
root -> Fail = NULL;
int f = 0, r = 0, i;
for (q[r++] = root; f - r;)
for (Now = q[f++], f &= SIZE, i = 0; i < 26; ++i)
if (Now -> next[i])
{
Now -> next[i] -> Fail = root;
for (p = Now -> Fail; p; p = p -> Fail)
if (p -> next[i])
{
Now -> next[i] -> Fail = p -> next[i];
break;
} /* if */
q[r++] = Now -> next[i], r &= SIZE;
} /* if */
return;
} /* Build */
inline int query(const char* str)
{
int res = 0;
Trie* p = root;
while (*str)
{
int Index = *str++ - 'a';
while (!p -> next[Index] && p != root) p = p -> Fail;
p = p -> next[Index];
if (!p) p = root;
for (Trie* tmp = p; tmp -> cnt + 1 && tmp != root; tmp = tmp -> Fail)
res += tmp -> cnt, tmp -> cnt = -1;
} /* while */
return res;
} /* query */
int main()
{
freopen("Keywords.in" , "r", stdin );
freopen("Keywords.out", "w", stdout);
for (scanf("%d", &T); T--;)
{
scanf("%d", &n);
root = new Trie();
for (int i = 0; i < n; ++i)
scanf("%s", str), Ins(str);
Build();
scanf("%s", str);
printf("%d\n", query(str));
} /* for */
return 0;
} /* main */
/*
AC自动机模板题,不多说。
*/
hdu2896 病毒侵袭
/****************************\
* @prob: hdu2896 病毒袭击 *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: June. 23rd, 2012 *
* @memo: AC自动机 *
\****************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#include <bitset>
using std::bitset;
const int maxN = 510;
struct Trie
{
int ID; Trie* Fail; Trie* next[128];
Trie(): ID(0), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
} *root; int n, m;
char str[10010];
inline void Ins(const char* str, int ord)
{
Trie* p = root;
while (*str)
{
int Index = *str++;
if (!p -> next[Index])
p -> next[Index] = new Trie();
p = p -> next[Index];
} /* while */
p -> ID = ord; return;
} /* Ins */
inline void Build()
{
root -> Fail = NULL;
static const int SIZE = 0x3ffff;
static Trie* q[SIZE + 1];
static Trie* p;
static Trie* Now;
int f = 0, r = 0, i;
for (q[r++] = root; f - r;)
for (Now = q[f++], f &= SIZE, i = 0; i < 128; ++i)
if (Now -> next[i])
{
Now -> next[i] -> Fail = root;
for (p = Now -> Fail; p; p = p -> Fail)
if (p -> next[i])
{
Now -> next[i] -> Fail = p -> next[i];
break;
} /* if */
q[r++] = Now -> next[i], r &= SIZE;
} /* if */
return;
} /* Build */
inline void query(const char* str, bitset <maxN>& S)
{
S.reset(); Trie* p = root;
while (*str)
{
int Index = *str++;
while (!p -> next[Index] && p != root) p = p -> Fail;
p = p -> next[Index];
if (!p) p = root;
for (Trie* tmp = p; tmp != root; tmp = tmp -> Fail)
if (tmp-> ID) S.set(tmp -> ID);
} /* while */
return;
} /* query */
int main()
{
freopen("virus.in" , "r", stdin );
freopen("virus.out", "w", stdout);
scanf("%d", &n);
root = new Trie();
for (int i = 0; i < n; ++i)
{
scanf("%s", str);
Ins(str, i + 1);
} /* for */
Build();
scanf("%d", &m);
int cnt = 0;
for (int i = 0; i < m; ++i)
{
static bitset <maxN> S;
scanf("%s", str);
query(str, S);
if (S.count())
{
++cnt;
printf("web %d:", i + 1);
for (int j = 1; j < n + 1; ++j)
if (S.test(j)) printf(" %d", j);
printf("\n");
} /* if */
} /* for */
printf("total: %d\n", cnt);
return 0;
} /* main */
/*
AC自动机模板题。
注意题目中说到不同病毒的特征码不同。
*/
hdu3065 病毒侵袭持续中
/**********************************\
* @prob: hdu3065 病毒侵袭持续中 *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: June. 23rd, 2012 *
* @memo: AC自动机 *
\**********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
const int maxN = 1010;
struct Trie
{
int cnt; Trie* Fail; Trie* next[26];
Trie(): cnt(0), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
~Trie()
{
for (int i = 0; i < 26; ++i) if (next[i])
{
delete next[i];
next[i] = NULL;
} /* if */
} /* ~Trie */
} *root, *pos[maxN]; int n;
char virus[maxN][60], web[2000010];
inline void Ins(const char* str, int ord)
{
Trie* p = root;
while (*str)
{
int Index = *str++ - 'A';
if (!p -> next[Index])
p -> next[Index] = new Trie();
p = p -> next[Index];
} /* whlie */
pos[ord] = p; return;
} /* Ins */
inline void Build()
{
static const int SIZE = 0x3ffff;
static Trie* q[SIZE + 1];
static Trie* Now;
static Trie* p;
int f = 0, r = 0, i;
root -> Fail = NULL;
for (q[r++] = root; f - r;)
for (Now = q[f++], f &= SIZE, i = 0; i < 26; ++i)
if (Now -> next[i])
{
Now -> next[i] -> Fail = root;
for (p = Now -> Fail; p; p = p -> Fail)
if (p -> next[i])
{
Now -> next[i] -> Fail = p -> next[i];
break;
} /* if */
q[r++] = Now -> next[i], r &= SIZE;
} /* if */
return;
} /* Build */
inline void query(const char* str)
{
Trie* p = root;
while (*str)
{
int Index = *str++ - 'A';
if (Index < 0 || Index > 25) {p = root; continue;} /* if */
//!!注意遇到非大写字母时需要从头开始匹配。
while (!p -> next[Index] && p != root) p = p -> Fail;
p = p -> next[Index];
if (!p) p = root;
for (Trie* tmp = p; tmp != root; tmp = tmp -> Fail)
++tmp -> cnt;
} /* while */
return;
} /* query */
int main()
{
freopen("virus1.in" , "r", stdin );
freopen("virus1.out", "w", stdout);
while (~scanf("%d", &n))
{
root = new Trie();
for (int i = 0; i < n; ++i)
{
scanf("%s", virus[i]);
Ins(virus[i], i);
} /* for */
Build();
scanf("%s", web);
query(web);
for (int i = 0; i < n; ++i) if (pos[i] -> cnt)
printf("%s: %d\n", virus[i], pos[i] -> cnt);
delete root;
} /* while */
return 0;
} /* main */
/*
AC自动机模板题,不多说。
*/
zoj3430 Detect the Virus
/************************************\
* @prob: zoj3430 Detect the Virus *
* @auth: Wang Junji *
* @stat: Acceped. *
* @date: June. 23rd, 2012 *
* @memo: AC自动机 *
\************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
class Trie
{
private: int cnt, ID; Trie* Fail; Trie* next[256];
public:
static int tot;
Trie(): cnt(0), ID(0), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
~Trie()
{
for (int i = 0; i < 256; ++i) if (next[i]) delete next[i];
next[0] = next[1] = NULL;
} /* ~Trie */
static Trie* NewTrie()
{
Trie* tmp = new Trie();
tmp -> ID = tot++;
return tmp;
} /* NewTrie */
void Ins(int* fir, int* la)
{
Trie* p = this;
while (fir != la)
{
int Index = *fir++;
if (!p -> next[Index]) p -> next[Index] = NewTrie();
p = p -> next[Index];
} /* while */
++p -> cnt; return;
} /* Ins */
void Build()
{
static const int SIZE = 0x3ffff;
static Trie *q[SIZE + 1], *Now, *p;
Fail = NULL;
int f = 0, r = 0, i;
for (q[r++] = this; f != r;)
for (Now = q[f++], f &= SIZE, i = 0; i < 256; ++i)
if (Now -> next[i])
{
Now -> next[i] -> Fail = this;
for (p = Now -> Fail; p; p = p -> Fail)
if (p -> next[i])
{
Now -> next[i] -> Fail = p -> next[i];
break;
} /* if */
q[r++] = Now -> next[i], r &= SIZE;
} /* if */
return;
} /* Build */
int query(int* fir, int* la)
{
static bool marked[30010];
memset(marked, 0, sizeof marked);
Trie* p = this; int res = 0;
while (fir != la)
{
int Index = *fir++;
while (!p -> next[Index] && p != this) p = p -> Fail;
p = p -> next[Index];
if (!p) p = this;
for (Trie* tmp = p; tmp != this && !marked[tmp -> ID]; tmp = tmp -> Fail)
res += tmp -> cnt, marked[tmp -> ID] = true;
} /* while */
return res;
} /* query */
};
const char* const base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bin[16384], mp[128], n, m, Trie::tot = 0;
char str[2010];
inline int* decode(const char* str, int* fir)
{
int len = 0;
while (*str)
{
if (*str == '=') len -= 2;
else
{
int ths = mp[(int)*str];
for (int i = 6; i--;)
fir[len++] = (ths >> i) & 1;
} /* else */
++str;
} /* while */
len >>= 3;
for (int i = 0; i < len; ++i)
{
int ths = 0;
for (int j = 0; j < 8; ++j)
(ths <<= 1) |= fir[i << 3 | j];
fir[i] = ths;
}
return fir + len;
} /* decode */
inline void calc_mp()
{
for (const char* tmp = base64; *tmp; ++tmp)
mp[(int)*tmp] = tmp - base64;
return;
} /* calc_mp */
int main()
{
freopen("virus.in" , "r", stdin );
freopen("virus.out", "w", stdout);
calc_mp();
while (~scanf("%d", &n))
{
Trie::tot = 0;
Trie* AC = Trie::NewTrie();
for (int i = 0; i < n; ++i)
{
scanf("%s", str);
int len = decode(str, bin) - bin;
AC -> Ins(bin, bin + len);
} /* for */
AC -> Build();
scanf("%d", &m);
for (int i = 0; i < m; ++i)
{
scanf("%s", str);
int len = decode(str, bin) - bin;
printf("%d\n", AC -> query(bin, bin + len));
} /* for */
puts("");
delete AC;
} /* while */
return 0;
} /* main */
/*
AC自动机模板题。
解码是将其解码成原字符串而不是二进制数……
*/
AC自动机 + 矩阵
/********************************\
* @prob: poj2778 DNA sequence *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: June. 23rd, 2012 *
* @memo: AC自动机、矩阵乘法 *
\********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
const int maxN = 210, MOD = 100000;
struct Matrix
{
int ele[maxN][maxN]; static int sz;
Matrix() {memset(ele, 0, sizeof ele);} /* Matrix */
Matrix(const Matrix& b) {memcpy(ele, b.ele, sizeof ele);} /* Matrix */
int* const operator[](const int& Index) {return ele[Index];} /* operator[] */
const int* const operator[](const int& Index) const {return ele[Index];} /* operator[] */
Matrix& operator=(const Matrix& b)
{memcpy(ele, b.ele, sizeof ele); return *this;}
/* operator= */
Matrix& operator*=(const Matrix& b)
{
Matrix res;
for (int i = 0; i < sz; ++i)
for (int j = 0; j < sz; ++j)
{
long long tmp = 0;
for (int k = 0; k < sz; ++k)
tmp += (long long)ele[i][k] * b[k][j];
res[i][j] = (int)(tmp % MOD);
} /* for */
return *this = res;
} /* operator*= */
} g;
struct Trie
{
bool danger; Trie *next[4], *Fail;
Trie(): danger(0), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
} root[maxN], *__trie = root + 1;
int n, m, Matrix::sz = 0;
char str[20];
inline int get_index(const char& ch)
{
switch (ch)
{
case 'A': return 0;
case 'C': return 1;
case 'G': return 2;
case 'T': return 3;
} /* switch */
return -1;
} /* get_index */
inline void Ins(const char* str)
{
Trie* p = root;
while (*str)
{
int Index = get_index(*str++);
if (!p -> next[Index])
p -> next[Index] = new (__trie++) Trie();
p = p -> next[Index];
} /* while */
p -> danger = true; return;
} /* Ins */
inline void Build()
{
static const int SIZE = 0x3ffff;
static Trie *q[SIZE + 1], *p;
int f = 0, r = 0, i; root -> Fail = root;
for (q[r++] = root; f - r;)
for (p = q[f++], f &= SIZE, p -> danger |= p -> Fail -> danger, i = 0; i < 4; ++i)
if (p -> next[i])
{
p -> next[i] -> Fail = p == root ? root : p -> Fail -> next[i];
q[r++] = p -> next[i], r &= SIZE;
} /* if */
else p -> next[i] = p == root ? root : p -> Fail -> next[i];
return;
} /* Build */
inline void pow(Matrix& g, const int& __n)
{
Matrix tmp(g), res;
for (int i = 0; i < Matrix::sz; ++i) res[i][i] = 1;
for (int n = __n; n; n >>= 1, tmp *= tmp)
if (n & 1) res *= tmp;
g = res; return;
} /* pow */
int main()
{
freopen("DNA.in" , "r", stdin );
freopen("DNA.out", "w", stdout);
scanf("%d%d", &m, &n);
while (m--)
{
scanf("%s", str);
Ins(str);
} /* while */
Build();
int tot = __trie - root;
Matrix::sz = tot;
for (int i = 0; i < tot; ++i)
if (!root[i].danger)
for (int j = 0; j < 4; ++j)
if (!root[i].next[j] -> danger)
++g[i][root[i].next[j] - root];
pow(g, n);
int ans = 0;
for (int i = 0; i < tot; ++i) (ans += g[0][i]) %= MOD;
printf("%d\n", ans); return 0;
} /* main */
/*
假设之前已经将N-1位基因安排好了,现在我们要在最后添加一位,使它变成N为的不含病毒的基因。
就拿样例来说:最后只可能以*或者*A结尾(*表示任意的但使之不携带病毒的碱基)。那么相应状态转移矩阵为:
3 1
0 0
也就是说,对于*状态,我们可以在后面添加A,使之变成*A,也可以在后面添加C、G或T,使之变成*;
对于*A状态,它既不能转移到*状态也不能转移到*A状态(因为不论在后面添加什么碱基都将使之携带病毒)。
那么构造初始矩阵的方法就出来了:
1) 建立Trie树;
2) 广度优先遍历Trie树,若某个结点的某个孩子指针为空,那么将其指向Fail指针对应的孩子处,(若该结点为根,那么指向根结点。例如若一个结点(非根)只有A、C、G三个子结点,那么将T对应的孩子指针指向该结点的Fail指针对应的孩子T。)表示可以进行这样的状态转移。
另外,若一个结点的Fail指针被标记为携带病毒,那么它也应该被标记为携带病毒。
于是初始矩阵就为所有不携带病毒的结点向所有不携带病毒的结点转移的状态数。
得到初始矩阵之后,将其自乘N次(表示在空串后面添加N个字符),最后将*状态向所有状态的转移值累加即可。
*/
hdu2243 考研路茫茫——单词情结
/****************************************\
* @prob: hdu2243 考研路茫茫——单词情结 *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: june. 24th, 2012 *
* @memo: AC自动机、矩阵乘法 *
\****************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
typedef unsigned long long LLU;
const int maxN = 30; //这个值一定不能开大了,否则爆栈。
class Matrix
{
private:
LLU ele[maxN][maxN]; static int sz;
public:
Matrix(const int& n = 0)
{
memset(ele, 0, sizeof ele);
if (n == 1) for (int i = 0; i < sz; ++i) ele[i][i] = 1;
} /* Matrix */
Matrix(const Matrix& b) {memcpy(ele, b.ele, sizeof ele);} /* Matrix */
static void setsz(const int& n = 0) {sz = n; return; } /* setsz */
static int getsz( ) { return sz;} /* getsz */
LLU* const operator[](const int& Index) {return ele[Index];} /* operator[] */
const LLU* const operator[](const int& Index) const {return ele[Index];} /* operator[] */
Matrix& operator=(const Matrix& b)
{memcpy(ele, b.ele, sizeof ele); return *this;}
/* operator= */
Matrix& operator*=(const Matrix& b) {return *this = *this * b;} /* operator*= */
friend const Matrix& operator+(const Matrix&, const Matrix&);
friend const Matrix& operator*(const Matrix&, const Matrix&);
} g;
struct Trie
{
bool danger; Trie* Fail; Trie* next[26];
Trie(): danger(0), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
} root[maxN], *__trie = root; int n, m, Matrix::sz = 0; char str[maxN];
inline const Matrix& operator+(const Matrix& a, const Matrix& b)
{
static Matrix c;
for (int i = 0; i < Matrix::sz; ++i)
for (int j = 0; j < Matrix::sz; ++j)
c[i][j] = a[i][j] + b[i][j];
return c;
} /* operator+ */
inline const Matrix& operator*(const Matrix& a, const Matrix& b)
{
static Matrix c;
for (int i = 0; i < Matrix::sz; ++i)
for (int j = 0; j < Matrix::sz; ++j)
{
LLU tmp = 0;
for (int k = 0; k < Matrix::sz; ++k)
tmp += a[i][k] * b[k][j];
c[i][j] = tmp;
} /* for */
return c;
} /* operator* */
inline void Ins(const char* str)
{
Trie* p = root;
while (*str)
{
int Index = *str++ - 'a';
if (!p -> next[Index])
p -> next[Index] = new (__trie++) Trie();
p = p -> next[Index];
} /* while */
p -> danger = true; return;
} /* Ins */
inline void Build()
{
static const int SIZE = 0x3ffff;
static Trie *q[SIZE + 1], *p;
int f = 0, r = 0, i; root -> Fail = root;
for (q[r++] = root; f - r;)
for (p = q[f++], f &= SIZE, p -> danger |= p -> Fail -> danger, i = 0; i < 26; ++i)
//注意danger标记的维护。
if (p -> next[i])
{
p -> next[i] -> Fail = p == root ? root : p -> Fail -> next[i];
q[r++] = p -> next[i], r &= SIZE;
} /* if */
else p -> next[i] = p == root ? root : p -> Fail -> next[i];
return;
} /* Build */
template <class _Tp>
inline _Tp pow(const _Tp& a, const int& __n)
{
_Tp tmp(a), res(1);
for (int n = __n; n; n >>= 1, tmp *= tmp)
if (n & 1) res *= tmp;
return res;
} /* pow */
template <class _Tp>
_Tp calc(const _Tp& a, const int& n)
{
if (n == 0) return 0;
if (n == 1) return a;
static _Tp t1, t2;
t1 = calc(a, n >> 1), t2 = pow(a, n >> 1);
if (n & 1) return t1 + t2 * (t1 + t2 * a);
else return t1 + t2 * t1;
} /* calc */
int main()
{
freopen("word.in", "r", stdin);
freopen("word.out", "w", stdout);
while (~scanf("%d%d", &m, &n))
{
new ((__trie = root)++) Trie();
while (m--) scanf("%s", str), Ins(str);
Build();
int tot = __trie - root;
Matrix::setsz(tot); g = 0;
for (int i = 0; i < tot; ++i)
if (!root[i].danger)
for (int j = 0; j < 26; ++j)
if (!root[i].next[j] -> danger)
++g[i][root[i].next[j] - root];
LLU res = calc(26LLU, n);
g = calc(g, n);
for (int i = 0; i < Matrix::getsz(); ++i) res -= g[0][i];
printf("%llu\n", res);
} /* while */
return 0;
} /* main */
/*
先按照DNA sequence中的做法,处理出不包含所有单词串的初始矩阵A,
计算A + A ^ 2 + A ^ 3 + A ^ 4 + ... + A ^ N。
然后用26 + 26 ^ 2 + 26 ^ 3 + 26 ^ 4 + ... + 26 ^ N减去不包含所有单词串的情况即可。
至于取余,直接用unsigned long long即可。
*/
AC自动机+DP
/*************************************\
* @prob: hdu2825 Wireless Password *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: June, 24th, 2012 *
* @memo: AC自动机、状压DP *
\*************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
const int maxN = 110, MOD = 20090717;
struct Trie;
typedef Trie* Trie_ptr;
struct Trie
{
unsigned val; Trie_ptr Fail, next[26];
Trie(): val(0U), Fail(NULL) {memset(next, 0, sizeof next);} /* Trie */
} root[maxN]; Trie_ptr __trie; int f[30][maxN][1 << 10], n, m, K; char str[20];
inline void Ins(const char* str, int ord)
{
Trie_ptr p = root;
while (*str)
{
int Index = *str++ - 'a';
if (!p -> next[Index])
p -> next[Index] = new (__trie++) Trie();
p = p -> next[Index];
} /* while */
p -> val |= 1U << ord; return;
} /* Ins */
inline void Build()
{
static const int SIZE = 0x3ffff;
static Trie_ptr q[SIZE + 1], p;
int f = 0, r = 0, i; root -> Fail = root;
for (q[r++] = root; f - r;)
for (p = q[f++], f &= SIZE, p -> val |= p -> Fail -> val, i = 0; i < 26; ++i)
if (p -> next[i])
{
p -> next[i] -> Fail = p == root ? root : p -> Fail -> next[i];
q[r++] = p -> next[i], r &= SIZE;
} /* if */
else p -> next[i] = p == root ? root : p -> Fail -> next[i];
return;
} /* Build */
inline int ones(const unsigned& __S)
{
int res = 0;
for (unsigned S = __S; S; S >>= 1) res += S & 1;
return res;
} /* ones */
int main()
{
freopen("password.in" , "r", stdin );
freopen("password.out", "w", stdout);
while (~scanf("%d%d%d", &n, &m, &K) && (n || m || K))
{
new ((__trie = root)++) Trie();
for (int i = 0; i < m; ++i) scanf("%s", str), Ins(str, i);
Build(); int tot = __trie - root;
for (int i = 0; i < n + 1; ++i)
for (int j = 0; j < tot; ++j)
for (unsigned S = 0; S < 1U << m; ++S)
f[i][j][S] = 0;
f[0][0][0] = 1;
for (int i = 0; i < n; ++i)
for (int j = 0; j < tot; ++j)
for (unsigned ths = 0; ths < 1U << m; ++ths)
if (f[i][j][ths])
for (int k = 0; k < 26; ++k)
(f[i + 1][root[j].next[k] - root][root[j].next[k] -> val | ths] += f[i][j][ths]) %= MOD;
int res = 0;
for (int j = 0; j < tot; ++j)
for (unsigned S = 0; S < 1U << m; ++S)
if (ones(S) >= K) (res += f[n][j][S]) %= MOD;
printf("%d\n", res);
} /* while */
return 0;
} /* main */
/*
用AC自动机建立状态转移,然后直接递推。
清零要用for循环清零。
*/