HDU 2243考研路茫茫——单词情结(ac自动机构造dp矩阵+快速幂和公式(矩阵/数))

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/84875743

考研路茫茫——单词情结

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;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/84875743