题目大意:
就是现在给出N个字符 ( ASCII 码在32以上 )代表一种语言中的N个字母( 1 <= N <= 50) 然后是P ( 0 <= P <= 50 )个串(由给出的N个字符的字符集中的字符组成,现在要求长度为M( 1 <= M <= 50) 的由N个字符的字符集中的字符组成的串,且不包含给出的P个串的不同的串的数量。
大致思路:首先这题和 HDU 2243 以及 POJ 2778比较像,但是这道题不方便使用矩阵乘法 ( 高精度以及内存限制 ) ,直接用高精度的数组的dp来解决即可,思路是先找到转移状态的矩阵,然后滚动数组进行DP即可。
注意这里高精度需要压位,一个int存储一个个位数字的做法容易TLE。
代码如下:
Result : Accepted Memory : 744 KB Time : 688 ms
/*
* Author: Gatevin
* Created Time: 2014/11/20 13:38:52
* File Name: Kagome.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
int n, m, p;
const int mod = 10000;//高精度计算中一个int存储4位数字
map <char, int> M;
struct bign//高精度运算类,需要压位,不然容易MLE和TLE
{
int len;
int s[30];
bign()
{
len = 0;
memset(s, 0, sizeof(s));
return;
}
bign(int num)
{
memset(s, 0, sizeof(s));
len = 0;
while(num)
{
s[len] = num % mod;
num /= mod;
len++;
}
return;
}
bign operator = (int num);
bign operator + (const bign& plu);
bign operator * (const bign& tim);
friend ostream& operator << (ostream& out, const bign& num);
};
bign bign :: operator = (int num)
{
*this = bign(num);
return *this;
}
bign bign :: operator + (const bign& plu)
{
bign ret;
ret.len = max(this->len + 1, plu.len + 1);
int c = 0;
for(int i = 0; i <= ret.len; i++)
{
ret.s[i] = (this->s[i] + plu.s[i] + c) % mod;
c = (this->s[i] + plu.s[i] + c) / mod;
}
while(ret.len != 0 && ret.s[ret.len - 1] == 0) ret.len--;
return ret;
}
bign bign :: operator * (const bign& tim)
{
bign ret;
ret.len = tim.len + this->len + 1;
int c = 0;
for(int i = 0; i <= this->len; i++)
{
c = 0;
for(int j = 0; j <= tim.len; j++)
{
int tmp = (ret.s[i + j] + c + tim.s[j]*this->s[i]);//之前这里犯了一下低级错误,WA了几下才发现..
ret.s[i + j] = tmp % mod;
c = tmp / mod;
}
ret.s[i + tim.len + 1] = (ret.s[i + tim.len + 1] + c);
}
while(ret.len != 0 && ret.s[ret.len - 1] == 0) ret.len--;
return ret;
}
ostream& operator << (ostream& out, const bign& num)
{
if(num.len == 0)
{
printf("0"); return out;
}
for(int i = num.len - 1; i >= 0; i--)
{
if(i != num.len - 1)
printf("%04d", num.s[i]);
else printf("%d", num.s[i]);
}
return out;
}
bign dp[101][2];//滚动数组
struct Trie
{
int next[101][50], fail[101];
bool end[101], vis[101];
int L, root;
int newnode()
{
for(int i = 0; i < n; i++)
next[L][i] = -1;
end[L++] = 0;
return L - 1;
}
void init()
{
L = 0;
root = newnode();
return;
}
void insert(char *s)
{
int now = root;
for(; *s; s++)
{
if(next[now][M[*s]] == -1)
next[now][M[*s]] = newnode();
now = next[now][M[*s]];
}
end[now] = 1;
return;
}
void build()//建立状态转移图
{
queue <int> Q;
fail[root] = root;
Q.push(root);
while(!Q.empty())
{
int now = Q.front();
Q.pop();
if(end[fail[now]]) end[now] = 1;
for(int i = 0; i < n; i++)
if(next[now][i] == -1)
next[now][i] = now == root ? root : next[fail[now]][i];
else
{
fail[next[now][i]] = now == root ? root : next[fail[now]][i];
Q.push(next[now][i]);
}
}
return;
}
void solve()
{
int mat[101][101];
memset(mat, 0, sizeof(mat));
queue <int> Q;
memset(vis, 0, sizeof(vis));
Q.push(root);
vis[root] = 1;
while(!Q.empty())//完成转移的矩阵
{
int now = Q.front();
Q.pop();
for(int i = 0; i < n; i++)
if(!end[next[now][i]])
{
mat[now][next[now][i]]++;
if(!vis[next[now][i]])
{
vis[next[now][i]] = 1;
Q.push(next[now][i]);
}
}
}
int now = 0;
dp[0][now] = 1;
for(int i = 1; i < L; i++)
dp[i][now] = 0;
for(int i = 1; i <= m; i++)//受到内存限制,用滚动数组来计算
{
now ^= 1;
for(int j = 0; j < L; j++) dp[j][now] = 0;
for(int j = 0; j < L; j++)
for(int k = 0; k < L; k++)
dp[j][now] = dp[j][now] + dp[k][now^1]*mat[k][j];
}
bign ans = 0;
for(int i = 0; i < L; i++)
ans = ans + dp[i][now];
cout<<ans<<endl;
}
};
Trie AC;
int main()
{
char ts[11];
while(~scanf("%d %d %d", &n, &m, &p))
{
AC.init();
M.clear();
getchar();
for(int i = 0; i < n; i++)
{
char tmp = getchar();
M[tmp] = i;
}
while(p--)
{
scanf("%s", ts);
AC.insert(ts);
}
AC.build();
AC.solve();
}
return 0;
}