zoukankan      html  css  js  c++  java
  • HDU 2243 ( Trie图 矩阵构造幂和 )

    题意 :  长度不超过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。

    分析: 我们可以用Tire图跑矩阵快速幂的方法,去求长度为n不包含给定单词的词为sum; 所以想到求:长度为n包含给定单词的词 的算法就是用总的方案数-长度为n不包含给定单词的词的方案数为26^n-sum;  这题的难点是求长度不超过L的方案数,就是说我们需要求 26-sum1+26^2-sum2+26^3-sum3......26^n-sumn = (26+26^2+...26^n)-(sum1+sum2+...sumn); 我们显然不是遍历求; 考虑优先算法:

     假设原 Trie 图构建出来的状态矩阵为 A ,那么同样的我们需要构造一个幂和即 A1 + A2 + A+ ..... + A然后最后的答案便是 ∑AL(0, i)  ( i ∈ 1~矩阵长度 ) ,那怎么去构造这两个幂和呢?

     只要利用这个公式即可,用原矩阵 + 单位矩阵 + 零矩阵构造出新矩阵,最后右上角的矩阵便是幂和的矩阵

    需要注意的点:(1)在求(26+26^2+...26^n)的时候不能用等比数列公式去求,这样会有误差,可以用上面构造的矩阵的方法构造

    | 26,1 |

    |0 , 1 |  的跑矩阵快速幂

    (2 对于 2^64次方求模) 直接开unsigned long long 就好

    #include<string.h>
    #include<stdio.h>
    #include<iostream>
    #include<queue>
    #define ULL unsigned long long
    using namespace std;
    
    const int Max_Tot = 1e2 + 10;
    const int Letter  = 26;
    int maxn;///矩阵的大小
    char S[11];
    
    struct mat{ ULL m[111][111]; }unit, M;
    mat operator * (mat a, mat b){
        mat ret;
        for(int i=0; i<maxn; i++){
            for(int j=0; j<maxn; j++){
                ret.m[i][j] = (ULL)0;
                for(int k=0; k<maxn; k++){
                    ret.m[i][j] += a.m[i][k]*b.m[k][j];
                }
            }
        }
        return ret;
    }
    
    inline void init_unit() {
        for(int i=0; i<maxn; i++)
            unit.m[i][i] = 1;
    }
    
    mat pow_mat(mat a, long long n){
        mat ret = unit;
        while(n){
            if(n&1) ret = ret * a;
            a = a*a;
            n >>= 1;
        }
        return ret;
    }
    
    struct Aho{
        struct StateTable{
            int Next[Letter];
            int fail, flag;
        }Node[Max_Tot];
        int Size;
        queue<int> que;
    
        inline void init(){
            while(!que.empty()) que.pop();
            memset(Node[0].Next, 0, sizeof(Node[0].Next));
            Node[0].fail = Node[0].flag = 0;
            Size = 1;
        }
    
        inline void insert(char *s){
            int now = 0;
            for(int i=0; s[i]; i++){
                int idx = s[i] - 'a';
                if(!Node[now].Next[idx]){
                    memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                    Node[Size].fail = Node[Size].flag = 0;
                    Node[now].Next[idx] = Size++;
                }
                now = Node[now].Next[idx];
            }
            Node[now].flag = 1;
        }
    
        inline void BuildFail(){
            Node[0].fail = -1;
            for(int i=0; i<Letter; i++){
                if(Node[0].Next[i]){
                    Node[Node[0].Next[i]].fail = 0;
                    que.push(Node[0].Next[i]);
                }else Node[0].Next[i] = 0;///必定指向根节点
            }
            while(!que.empty()){
                int top = que.front(); que.pop();
                if(Node[Node[top].fail].flag) Node[top].flag = 1;
                for(int i=0; i<Letter; i++){
                    int &v = Node[top].Next[i];
                    if(v){
                        que.push(v);
                        Node[v].fail = Node[Node[top].fail].Next[i];
                    }else v = Node[Node[top].fail].Next[i];
                }
            }
        }
    
        inline void BuildMatrix(){
            for(int i=0; i<Size; i++)
                for(int j=0; j<Size; j++)
                    M.m[i][j] = 0;
            for(int i=0; i<Size; i++){
                for(int j=0; j<Letter; j++){
                    if(!Node[i].flag && !Node[ Node[i].Next[j] ].flag)
                        M.m[i][Node[i].Next[j]]++;
                }
            }
            maxn = Size;
        }
    }ac;
    
    ULL GetSum(long long num){
        mat ret;
        ret.m[0][0] = 26;
        ret.m[0][1] = 1;
        ret.m[1][0] = 0;
        ret.m[1][1] = 1;
        int tmp = maxn;
        maxn = 2;
        ret = pow_mat(ret, ++num);
        maxn = tmp;
        return ret.m[0][1]-1;
    }
    
    ULL GetElimination(long long num){
        mat tmp;
        for(int i=0; i<maxn; i++)///左上角 为 原矩阵
            for(int j=0; j<maxn; j++)
                tmp.m[i][j] = M.m[i][j];
    
        for(int i=0; i<maxn; i++)///右上角 为 单位矩阵
            for(int j=maxn; j<(maxn<<1); j++)
                tmp.m[i][j] = (i+maxn == j);
    
        for(int i=maxn; i<(maxn<<1); i++)///左下角 为 零矩阵
            for(int j=0; j<maxn; j++)
                tmp.m[i][j] = 0;
    
        for(int i=maxn; i<(maxn<<1); i++)///右下角 为 单位矩阵
            for(int j=maxn; j<(maxn<<1); j++)
                tmp.m[i][j] = (i==j);
    
        int Temp = maxn;
        maxn <<= 1;///先将原本矩阵的大小放大一倍进行快速幂运算,这个和我快速幂的写法有关
        tmp = pow_mat(tmp, ++num);
        ULL ret = (ULL)0;
        maxn = Temp;///再回复成原来大小
        for(int i=maxn; i<(maxn<<1); i++)///右上角的矩阵就是幂和了
            ret += tmp.m[0][i];
    
        return (--ret);///需要 -1
    }
    
    int main(void)
    {
        int n, m;
    
        while(~scanf("%d %d", &m, &n)){
            ac.init();
            for(int i=0; i<m; i++){
                scanf("%s", S);
                ac.insert(S);
            }
            ac.BuildFail();
            ac.BuildMatrix();
            init_unit();
            ULL Tot = GetSum((long long)n);///注意是传long long不然会爆int
            ULL Elimination = GetElimination((long long)n);
            cout<<Tot-Elimination<<endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    第四百六十九天 how can I 坚持
    第四百六十八天 how can I 坚持
    第四百四十六、七天 how can I 坚持
    第四百四十四、五天 how can I 坚持
    《Java练习题》Java进阶练习题(一)
    《Java练习题》Java习题集五
    《Java知识应用》Java加密方式(MD5)详解
    《Java知识应用》Java加密方式(Base64)详解
    《MySQL数据库》MySQL数据库安装(linux)
    《Java知识应用》Java Json说明和使用(fastjson)
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/11615477.html
Copyright © 2011-2022 走看看