考研路茫茫——单词情结
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7165 Accepted Submission(s): 2510
Problem Description
背单词,始终是复习英语的重要环节。在荒废了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个字符串,问你长度为1-n的字符串中至少包含一个上述字符串的数量('a'-'z')
解析:
详细原理请看链接
这道题跟上面那道题区别就是1.这道题求得是至少包含一个,那么我们求反面一个都不包含,再用总数去减就一样 了
总数为res=26^1+26^2+...+26^n
2.这道题长度为1..n,那么式子就变成了mat^1+mat^2+mat^3+...+mat^n
(mat为ac构造的矩阵)然后我们算出不包含的串的数量,ans+=mat[0][i] (i=0...cnt)
最后res-ans就是答案了。
这里上面那个式子分别可以用快速幂和公式来求
例如mat+... mat^6 = mat+mat^2+mat^3 + mat^3*(mat+mat^2+mat^3)
数字也是一样的
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<set>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
typedef unsigned long long ll;
using namespace std;
const int N = 1e3+100;
const int MAX = 500;
const int C = 'A';
const int NUM = 26;
struct Tree//字典树
{
int fail;//失配指针
int vis[NUM];//子节点的位置
int end;//标记有几个单词以这个节点结尾
int id;
}AC[MAX];//Trie树
typedef struct {
ll m[30][30];
int sizx,sizy;
}Matrix;
Matrix E_one;
inline Matrix Mul(Matrix a, Matrix b)
{
Matrix c;
memset(c.m, 0, sizeof(c.m));
c.sizx=a.sizx;
c.sizy=b.sizy;
for (int i = 0; i < a.sizx; i++)
{
for (int j = 0; j < b.sizy; j++)
{
for (int k = 0; k < a.sizy; k++)
{
c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) ) ;
}
}
}
return c;
}
inline Matrix fastm(Matrix a, ll num)
{
Matrix res;
memset(res.m, 0, sizeof(res.m));
res.sizx=a.sizx;
res.sizy=a.sizy;
for(int i=0;i<a.sizx;i++)
res.m[i][i]=1;
while (num)
{
if (num & 1)
res = Mul(res, a);
num >>= 1;
a = Mul(a, a);
}
return res;
}
inline int _trans(char c)
{
return c-'a';
}
int cnt=0;//Trie的指针
inline void Build(char *s)
{
int l=strlen(s);
int now=0;//字典树的当前指针
for(int i=0;i<l;++i)//构造Trie树
{
if(AC[now].vis[_trans(s[i])]==0)//Trie树没有这个子节点
AC[now].vis[_trans(s[i])]=++cnt;//构造出来
now=AC[now].vis[_trans(s[i])];//向下构造
}
AC[now].end++;//标记单词结尾
}
void Get_fail()//构造fail指针
{
queue<int> Q;//队列
AC[0].fail=0; //!!!
for(int i=0;i<NUM;++i)//第二层的fail指针提前处理一下
{
if(AC[0].vis[i]!=0)
{
AC[AC[0].vis[i]].fail=0;//指向根节点
Q.push(AC[0].vis[i]);//压入队列
}
else
{
AC[0].vis[i]=0; //指向根节点!!
}
}
while(!Q.empty())//BFS求fail指针
{
int u=Q.front();
Q.pop();
AC[u].end|=AC[AC[u].fail].end; //!!将单词的结尾都更新到各个被包含的子串中
for(int i=0;i<NUM;++i)//枚举所有子节点
{
if(AC[u].vis[i]!=0)//存在这个子节点
{
AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i];
//子节点的fail指针指向当前节点的
//fail指针所指向的节点的相同子节点
Q.push(AC[u].vis[i]);//压入队列
}
else//不存在这个子节点
AC[u].vis[i]=AC[AC[u].fail].vis[i];
//当前节点的这个子节点指向当
//前节点fail指针的这个子节点
}
}
}
Matrix getMatrix_ac() //已经将所有的虚结点都指向root
{
Matrix c;
memset(c.m,0,sizeof(c.m));
c.sizx=cnt+1;
c.sizy=cnt+1;
for(int i=0;i<=cnt;i++)
{
for(int j=0;j<NUM;j++)
{
if(!AC[AC[i].vis[j]].end)
c.m[i][AC[i].vis[j]]++;
}
}
return c;
}
inline Matrix Add(Matrix a, Matrix b)
{
Matrix c;
//memset(c.m, 0, sizeof(c.m));
c.sizx=a.sizx;
c.sizy=a.sizy;
for (int i = 0; i < a.sizx; i++)
{
for (int j = 0; j < a.sizy; j++)
{
c.m[i][j]=(a.m[i][j]+b.m[i][j]);
}
}
return c;
}
Matrix Matrix_Powsum(Matrix E,ll k) //快速幂和——矩阵
{
Matrix ans;
if(k==1)
return E;
Matrix P=fastm(E,k>>1);
Matrix S=Matrix_Powsum(E,k>>1);
ans=Mul(S,Add(P,E_one));
if(k&1)
ans=Add(ans,fastm(E,k));
return ans;
}
ll power(ll x , ll k)
{ ll ret(1) ;
while(k){
if(k&1)
ret = ret * x ;
x = x * x ;
k = k >> 1 ;
}
return ret ;
}
ll Powsum(ll x,ll k) //快速幂和——数
{
if(k==1) return x;
ll P=power(x,k>>1);
ll S=Powsum(x,k>>1);
ll ans=(P+1)*S;
if(k&1) ans=ans+power(x,k);
return ans;
}
char s[100];
int main()
{
int m,n;
while(scanf("%d%d",&m,&n)!=EOF){
cnt=0;
memset(AC,0,sizeof(AC));
getchar();
for(int i=1;i<=m;i++)
{
scanf("%s",s);
Build(s);
}
AC[0].fail = 0;//结束标志
Get_fail();//求出失配指针
E_one.sizx=cnt+1;
E_one.sizy=cnt+1;
memset(E_one.m,0,sizeof(E_one.m));
for(int i=0;i<E_one.sizx;i++)
E_one.m[i][i]=1;
ll res=Powsum(26,n);
Matrix ans=Matrix_Powsum(getMatrix_ac(),n);
for(int i=0;i<=cnt;i++)
res=(res-ans.m[0][i]);
printf("%llu\n",res);
}
return 0;
}