您的位置:首页 > 其它

HDU2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

2016-01-29 16:24 519 查看
POJ2778一样。这题是求长度不超过n且包含至少一个词根的单词总数。

长度不超过n的单词总数记为Sn,长度不超过n不包含词根的单词总数记为Tn。

答案就是,Sn-Tn。

Sn=26+262+263+...+26n

Tn=A+A2+A3+...+An (A为AC自动机构造出来的矩阵)

可以构造矩阵用快速幂求出Sn和Tn:

$$ \begin{bmatrix} 26 & 1 \\ 0 & 1 \end{bmatrix} \times \begin{bmatrix} S_n \\ 26 \end{bmatrix} = \begin{bmatrix} S_{n+1} \\ 26 \end{bmatrix}$$

$$ \begin{bmatrix} A & E \\ 0 & E \end{bmatrix} \times \begin{bmatrix} T_n \\ A \end{bmatrix} = \begin{bmatrix} T_{n+1} \\ A \end{bmatrix}$$

通过 $ \begin{bmatrix} 26 & 1 \\ 0 & 1 \end{bmatrix} ^n \times \begin{bmatrix} S_0 \\ 26 \end{bmatrix} = \begin{bmatrix} S_n \\ 26 \end{bmatrix}$ 即可得到Sn,其中S0=0,Tn同理。

另外题目结果mod 264这个直接把变量定义为unsigned __int64即可,溢出位数自动舍去。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int tn,ch[33][26],fail[33];
bool flag[33];
void insert(char *s){
int x=0;
for(int i=0; s[i]; ++i){
int y=s[i]-'a';
if(ch[x][y]==0) ch[x][y]=++tn;
x=ch[x][y];
}
flag[x]=1;
}
void init(int x){
memset(fail,0,sizeof(fail));
queue<int> que;
for(int i=0; i<26; ++i){
if(ch[0][i]) que.push(ch[0][i]);
}
while(!que.empty()){
int x=que.front(); que.pop();
for(int i=0; i<26; ++i){
if(ch[x][i]) que.push(ch[x][i]),fail[ch[x][i]]=ch[fail[x]][i];
else ch[x][i]=ch[fail[x]][i];
flag[ch[x][i]]|=flag[ch[fail[x]][i]];
}
}
}
void init(){
memset(fail,0,sizeof(fail));
queue<int> que;
for(int i=0; i<26; ++i){
if(ch[0][i]) que.push(ch[0][i]);
}
while(!que.empty()){
int now=que.front(); que.pop();
for(int i=0; i<26; ++i){
if(ch[now][i]) que.push(ch[now][i]),fail[ch[now][i]]=ch[fail[now]][i];
else ch[now][i]=ch[fail[now]][i];
flag[ch[now][i]]|=flag[ch[fail[now]][i]];
}
}
}
struct Mat{
unsigned long long m[66][66];
int n;
};
Mat operator*(const Mat &m1,const Mat &m2){
Mat m={0};
m.n=m1.n;
for(int i=0; i<m.n; ++i){
for(int j=0; j<m.n; ++j){
for(int k=0; k<m.n; ++k) m.m[i][j]+=m1.m[i][k]*m2.m[k][j];
}
}
return m;
}
int main(){
int n,l;
char str[6];
while(~scanf("%d%d",&n,&l)){
tn=0;
memset(ch,0,sizeof(ch));
memset(flag,0,sizeof(flag));
while(n--){
scanf("%s",str);
insert(str);
}
init();
Mat se={0},sm={0};
se.n=sm.n=2;
for(int i=0; i<2; ++i) se.m[i][i]=1;
sm.m[0][0]=26; sm.m[0][1]=1; sm.m[1][1]=1;
n=l;
while(n){
if(n&1) se=se*sm;
sm=sm*sm;
n>>=1;
}
unsigned long long tot=se.m[0][1]*26;

Mat te={0},tm={0};
te.n=tm.n=tn+1<<1;
for(int i=0; i<te.n; ++i) te.m[i][i]=1;
for(int i=0; i<=tn; ++i){
tm.m[i+tn+1][i+tn+1]=tm.m[i][i+tn+1]=1;
}
for(int i=0; i<=tn; ++i){
if(flag[i]) continue;
for(int j=0; j<26; ++j){
if(flag[ch[i][j]]) continue;
++tm.m[i][ch[i][j]];
}
}
Mat tmp=tm; tmp.n=tn+1;
n=l;
while(n){
if(n&1) te=te*tm;
tm=tm*tm;
n>>=1;
}
Mat tmp2; tmp2.n=tn+1;
for(int i=0; i<=tn; ++i){
for(int j=tn+1; j<te.n; ++j) tmp2.m[i][j-tn-1]=te.m[i][j];
}
tmp=tmp*tmp2;
unsigned long long res=0;
for(int i=0; i<=tn; ++i){
res+=tmp.m[0][i];
}
printf("%llu\n",tot-res);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: