hdu 2243 考研路茫茫——单词情结 AC自动机+转移矩阵

【题意】 每组数据占两行。     

            第一行有两个正整数N和L。(0<N<6,0<L<2^31)
            第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。
            求长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里不考虑单词是否有实际意义。

            题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=2243

 

【思路】 此题是poj 2778 的加强版,嫉妒恶心啊。。

            看到”至少“等字眼,如果正面做比较难的话,可以考虑从对立面思考,所谓正难则反嘛。

            因此,本题可以先求长度1~L的单词总数(即26^1+26^2+26^3+......+26^L),这可以利用等比数列的性质和二分求幂快速求得,

            然后,构造AC自动机,dp[i][j]表示在trie图从节点 i 到节点 j 的可能的情况数,求出状态矩阵,然后利用转移矩阵,

           对矩阵A^1+A^2+A^3+......+A^L求和。

 

View Code
#include<iostream>
using namespace std;
const int N=30;
const int kind=26;
struct TRIE{
int next[kind];
int fail,sign;
void init()
{
memset(next,0,sizeof(next));
fail=-1;sign=0;
}
}trie[N];
int que[N],head,tail;
int root,tot;
int n,L;

unsigned __int64 dp[N*2][N*2],s[N*2][N*2],tmp[N*2][N*2];
unsigned __int64 ans;

void init()
{
root=tot=0;
trie[0].init();
}
void insert(char *str)
{
int i=0,index,r;
r=root;
while(str[i])
{
index=str[i]-'a';
if(!trie[r].next[index])
{
trie[++tot].init();
trie[r].next[index]=tot;
}
r=trie[r].next[index];
i++;
}
trie[r].sign=1;
}

void build_ac_automation()
{
int i,son,now,p;
head=tail=0;
que[tail++]=root;
while(head!=tail)
{
now=que[head++];
for(i=0;i<kind;i++)
{
p=trie[now].fail;
if(trie[now].next[i])
{
son=trie[now].next[i];
if(now==root)
trie[son].fail=root;
else
{
trie[son].fail=trie[p].next[i];
if(trie[ trie[son].fail ].sign)
trie[son].sign++;
}
que[tail++]=son;
}
else
{
if(now==root)
trie[now].next[i]=root;
else
trie[now].next[i]=trie[p].next[i];
}
}
}
}
unsigned __int64 exp(unsigned __int64 a,unsigned __int64 b)
{
unsigned __int64 d=1,t=a;
while(b>0)
{
if(b&1)
d=d*t;
b>>=1;
t=t*t;
}
return d;
}
unsigned __int64 sum(unsigned __int64 p,unsigned __int64 n)
{
if(n==0)
return 1;
else if(n&1)
return (1+exp(p,n/2+1))*sum(p,n/2);
else
return (1+exp(p,n/2+1))*sum(p,(n-1)/2)+exp(p,n/2);
}
void multiply(unsigned __int64 a[][N*2],unsigned __int64 b[][N*2])
{
int i,j,k,sum=(tot+1)*2;
for(i=0;i<sum;i++)
{
for(j=0;j<sum;j++)
tmp[i][j]=0;
}
for(i=0;i<sum;i++)
{
for(j=0;j<sum;j++)
{
for(k=0;k<sum;k++)
{
tmp[i][j]+= a[i][k]*b[k][j];
}
}
}
for(i=0;i<sum;i++)
{
for(j=0;j<sum;j++)
b[i][j]=tmp[i][j];
}

}
void solve()
{
int i,j,son,sum;
sum=(tot+1)*2;
for(i=0;i<sum;i++)
{
for(j=0;j<sum;j++)
dp[i][j]=s[i][j]=0;
s[i][i]=1;
if(i>tot)
dp[i][i]=1;
}
for(i=0;i<=tot;i++)
{
if(!trie[i].sign)
{
for(j=0;j<kind;j++)
{
son=trie[i].next[j];
if(!trie[son].sign)
dp[i][son]++;
}
}
}
for(i=0;i<=tot;i++)
{
for(j=0;j<=tot;j++)
dp[i][j+tot+1]=dp[i][j];
}
while(L)
{
if(L&1)
multiply(dp,s);
L>>=1;
multiply(dp,dp);
}
// for(i=0;i<sum;i++)
// {
// for(j=0;j<sum;j++)
// printf("%I64u ",s[i][j]);
// cout<<endl;
// }
for(i=0;i<=tot;i++)
{
if(!trie[i].sign)
{
ans -= s[0][i+tot+1];
}
}

}
int main()
{
int i;
char str[10];
while(scanf("%d%d",&n,&L)!=EOF)
{
init();
for(i=0;i<n;i++)
{
scanf("%s",str);
insert(str);
}
build_ac_automation();
i=L;
ans=sum(26,i)-1;
solve();
printf("%I64u\n",ans);
}
return 0;
}



 

           

posted on 2012-03-11 17:55  孤星_bin  阅读(277)  评论(0编辑  收藏  举报

导航