您的位置:首页 > 其它

AC自动机专题——P - 考研路茫茫――单词情结 HDU - 2243 矩阵快速幂+AC自动机

2017-04-16 17:34 489 查看
背单词,始终是复习英语的重要环节。在荒废了3年大学生涯后,Lele也终于要开始背单词了。 

一天,Lele在某本单词书上看到了一个根据词根来背单词的方法。比如"ab",放在单词前一般表示"相反,变坏,离去"等。 

于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢。更确切的描述是:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义。 

比如一共有2个词根 aa 和 ab ,则可能存在104个长度不超过3的单词,分别为 

(2个) aa,ab, 

(26个)aaa,aab,aac...aaz, 

(26个)aba,abb,abc...abz, 

(25个)baa,caa,daa...zaa, 

(25个)bab,cab,dab...zab。 

这个只是很小的情况。而对于其他复杂点的情况,Lele实在是数不出来了,现在就请你帮帮他。 

Input本题目包含多组数据,请处理到文件结束。 

每组数据占两行。 

第一行有两个正整数N和L。(0<N<6,0<L<2^31) 

第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。 

Output对于每组数据,请在一行里输出一共可能的单词数目。 

由于结果可能非常巨大,你只需要输出单词总数模2^64的值。 

Sample Input
2 3
aa ab
1 2
a


Sample Output
104
52


题目大意:本题是求长度为n,包含模式串的字符串个数。直接用字符串总数减去不包含模式串的字符串个数即为所求。

因为对2^64取模,所以定义数据类型为unsigned long long就可以了,这样就实现了自动取模。

本题使用AC自动机类似得到状态转移的矩阵。

根据矩阵的性质

|A , 1|                            |A^n , 1+A^1+A^2+....+A^(n-1)| 

|0 , 1|   的n次方等         |    0 ,                                     1 | 

但是因为要求和。

所以在得到的L*L的矩阵中,需要增加一维,第L+1列全部为1。

字符串总数是26^1 + 26^2 + ......+ 26^m。

f
=1 + 26^1 + 26^2 +...26^n

f
=26*f[n-1]+1

{f
1} = {f[n-1] 1}[26 0;1 1]

数是f[L]-1;

此题的L<2^31.矩阵的幂不能是L+1次,否则就超时了

StatusAccepted
Time15ms
Memory1684kB
Length5162
LangG++
Submitted2017-04-14 09:38:25
Shared
RemoteRunId20411727
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <list>
#define rep(i,m,n) for(int i=m;i<=n;i++)
#define rsp(it,s) for(set<int>::iterator it=s.begin();it!=s.end();it++)
const int inf_int = 2e9;
const long long inf_ll = 2e18;
#define inf_add 0x3f3f3f3f

#define pb push_back
//#define mp make_pair
#define fi first
#define se second
#define pi acos(-1.0)
#define pii pair<int,int>
#define Lson L, mid, rt<<1
#define Rson mid+1, R, rt<<1|1
const int maxn=5e2+10;
using namespace std;
typedef vector<int> vi;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int ul;
inline int read(){int ra,fh;char rx;rx=getchar(),ra=0,fh=1;
while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();if(rx=='-')
fh=-1,rx=getchar();while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,
rx=getchar();return ra*fh;}
//#pragma comment(linker, "/STACK:102400000,102400000")
ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);}

const ll MOD =10330176681277348905LL;

ull qpow(ull p,ull q){
ull f=1;
while(q)
{
if(q&1) f=f*p;
p=p*p;
q>>=1;
}
return f;
}

struct Matrix{
ull mat[40][40];
int n;

Matrix(){}

Matrix(int _n){
n = _n;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
mat[i][j] = 0;
}

Matrix operator *(const Matrix &b) const{
Matrix ret = Matrix(n);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
for(int k = 0; k < n; k++)
ret.mat[i][j] += mat[i][k] * b.mat[k][j];
return ret;
}
};

Matrix pow_mat(Matrix a, int n){
Matrix ret = Matrix(a.n);
for(int i = 0; i < ret.n; i ++) ret.mat[i][i] = 1;
Matrix tmp = a;
while(n){
if(n & 1) ret = ret * tmp;
tmp = tmp * tmp;
n >>= 1;
}
return ret;
}

const int N = 1010;
const int M = 26;

class Trie
{

public:
int next
[M],fail
,end
;
int root,L;
int newnode()
{
for(int i = 0;i < M;i++)//每一个节点对应0-128中的任意一个。
next[L][i] = -1;
end[L++] = -1;//表示下面没有节点 初始化,如果是记录次数,就赋0 还可以赋任意的数,
return L-1;
}
void init()
{
L = 0;
root = newnode();
}
void insert(char s[])
{
int len = strlen(s);
int now = root;
for(int i = 0;i < len;i++)
{
if(next[now][s[i]-'a'] == -1)
next[now][s[i]-'a'] = newnode();
now=next[now][s[i]-'a'];//记录其对应的节点编号
}
end[now]=1;//记录当前匹配单词的节点
//end[now]++;也可以用匹配单词结束后来记录次数
}
void build()
{
queue<int>Q;
fail[root] = root;//根节点仍然是根节点
for(int i = 0;i < M;i++)//对第一个字符遍历
if(next[root][i] == -1)//没有此字符开头
next[root][i] = root;//跳转到根
else//有此字符开头的
{
fail[next[root][i]] = root;//这个行位的失败指针为根
Q.push(next[root][i]);//行放入队列
}
while(!Q.empty())//还有字符
{
int now = Q.front();//逐层拿出第一个
Q.pop();
if(end[fail[now]]== 1) end[now] = 1;
for(int i = 0;i < M;i++)//对这一行
if(next[now][i] == -1)//如果下一行没有这个字符
next[now][i] = next[fail[now]][i];//他的下一个的这
else//如果有这个字符
{
fail[next[now][i]] = next[fail[now]][i];//他的下一个的
Q.push(next[now][i]);//下一行继续
}
}
}

/*
* |A , 1| |A^n , 1+A^1+A^2+....+A^(n-1)|
|0 , 1| 的n次方等 | 0 , 1 |
但是因为要求和。
所以在L*L的矩阵中,需要增加一维,第L+1列全部为1。
字符串总数是26^1 + 26^2 + ......+ 26^m。
* */
Matrix getMatrix()
{
Matrix ret = Matrix(L+1);
memset(ret.mat,0,sizeof(ret.mat));
for(int i = 0;i < L;i++)
{
if(end[i]==1) continue;
for(int j = 0;j < 26;j++)
{
if(end[next[i][j]]==-1)
ret.mat[i][next[i][j]] ++;
}
}
for(int i = 0; i < L+1; i++) ret.mat[i][L] = 1; //关键,求其和
return ret;
}
};

Trie ac;
char buf[20];
int main()
{
int m;
int n;
while(scanf("%d%d",&m,&n) != EOF)
{
ac.init();
for(int i = 0; i < m; i++)
{
scanf("%s",buf);
ac.insert(buf);
}
ac.build();
Matrix ta = ac.getMatrix();
Matrix ret ;
//矩阵运算
ull ans = 0;
ret = pow_mat(ta,n);
for(int i = 0;i < ret.n;i++)
{
ans+=ret.mat[0][i];
}
ans--;

ret = Matrix(2);
ret.mat[0][0] = 26;
ret.mat[1][0] = ret.mat[1][1] = 1;
ret = pow_mat(ret, n);
ull ans1 = ret.mat[1][0] + ret.mat[0][0]; //字符串总个数
ans1 --;

ans = ans1-ans;
cout <<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: