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


    背单词,始终是复习英语的重要环节。在荒废了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
    思路:题目要求不超过L的所有符合单词,我们先考虑等于L
    对于所有满足的情况 = 所有情况-不满足情况
    不满足情况即是不包含词根的单词,对于计算满足长度所有情况,可以联想到矩阵快速幂,这样我们就只需要建一个矩阵图
    对于建图,我们知道,所有以词根作为前缀子串的都是不符合条件的。
    从根开始记录每种前缀可以添加的字母路径数(字母不是词根的结尾)。
    于是我们可以想到AC自动机,对于自动机补全的trie图,每个单词的fail都指向其最长子后缀
    也就是从根以fail指针向下建图的话,当遍历到一个词根末尾时,其下面的有词根都时不合法的,这可以用dfs维护。
    我们知道当矩阵中存有边权(代表路径数)时(相当于到每个点走一条路径),我们将自身乘自身,得到的二次幂矩阵,每个位置的权值就相当于从i走到j走两条路径的路径数目。(离散数学)
    这样推广到这,就相当于算出矩阵的L次幂,其第一行的和就是从0长度到L长度经过各个路径以各种字母结尾的路径和,这是长度等于L的情况。
    那么对于长度<=L的所有情况,我们知道 Σ(A1+A2+A3+...+AL),然后其第一行的和即是答案。
    一开始写的是递归,对于L=6为偶数,A1+A2+A3+A3(A1+A2+A3)
             对于L=5为奇数,A1+A2+A2(A1+A2)+A5
    但也许是姿势不对,一直tle。

    然后看了Discus,我们可以添加一维,(假设原本矩阵为L,此L!=题面的L),使得L+1列全是1,这样对于第L个矩阵maps【1】【L+1】就储存Σ(A1+A2+A3+...+A[L-1])
    对于取模,因为是对1<<64取模,太大了直接取不了(睿智的尝试),可以直接把相关数组开成unsign long long 可以直接对溢出舍去相当于取模
    还有利用等比数列公式时,由于涉及除法,利用欧拉定理求出逆元即可。

    附上丑代码
      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 
      4 typedef unsigned long long  ull;
      5 struct Node
      6 {
      7     int y,next;
      8 } node[41];
      9 int cnt,head[40],L;
     10 int n;
     11 ull len;
     12 int tohash[40],topos;
     13 void add(int x,int y)
     14 {
     15     node[++cnt].y=y;
     16     node[cnt].next=head[x];
     17     head[x]=cnt;
     18 }
     19 
     20 struct MAP
     21 {
     22     ull m[40][40];
     23     friend MAP operator*(const MAP a,const MAP b)
     24     {
     25         MAP tmp;
     26         for(int i=1; i<=L+1; i++)
     27         {
     28             for(int j=1; j<=L+1; j++)
     29             {
     30                 tmp.m[i][j] = 0;
     31 
     32                 for(int k=1; k<=L+1; k++)
     33                 {
     34                     tmp.m[i][j] = tmp.m[i][j]+ a.m[i][k]*b.m[k][j];
     35                 }
     36 
     37             }
     38         }
     39         return tmp;
     40     }
     41 //    friend MAP operator+(const MAP a,const MAP b)
     42 //    {
     43 //        MAP tmp;
     44 //        for(int i=1;i<=L;i++)
     45 //        {
     46 //            for(int j=1;j<=L+;j++)
     47 //            {
     48 //                tmp.m[i][j] = a.m[i][j] + b.m[i][j];
     49 //            }
     50 //        }
     51 //        return tmp;
     52 //    }
     53     void init()
     54     {
     55         for(int i=1; i<=L+1; i++)
     56         {
     57             for(int j=1; j<=L+1; j++)
     58             {
     59                 m[i][j] = 0;
     60             }
     61             m[i][i] = 1;
     62         }
     63     }
     64 } maps;
     65 struct ACTO
     66 {
     67     int trie[40][26],tot;
     68     int dp[40];
     69     int fail[40];
     70     int End[40];
     71     void init()
     72     {
     73         tot = cnt = L = topos = 0;
     74         memset(trie,0,sizeof(trie));
     75         memset(maps.m,0,sizeof(maps.m));
     76         memset(head,0,sizeof(head));
     77         memset(dp,0,sizeof(dp));
     78         memset(fail,0,sizeof(fail));
     79         memset(End,0,sizeof(End));
     80         memset(tohash,0,sizeof(tohash));
     81     }
     82     void Insert(char *str,int num)
     83     {
     84         int len = strlen(str),p=0;
     85         for(int i=0; i<len; i++)
     86         {
     87             int ch = str[i]-'a';
     88             if(!trie[p][ch])trie[p][ch]=++tot;
     89             p = trie[p][ch];
     90         }
     91         End[p]=num;
     92     }
     93     void get_fail()
     94     {
     95         queue<int>que;
     96         while(!que.empty())que.pop();
     97         for(int i=0; i<26; i++)if(trie[0][i])que.push(trie[0][i]);
     98         while(!que.empty())
     99         {
    100             int p = que.front();
    101             que.pop();
    102             for(int i=0; i<26; i++)
    103             {
    104                 if(trie[p][i])
    105                 {
    106                     fail[trie[p][i]] = trie[fail[p]][i];
    107                     que.push(trie[p][i]);
    108                 }
    109                 else trie[p][i] = trie[fail[p]][i];
    110             }
    111         }
    112     }
    113     void make_edge()
    114     {
    115         for(int i=1; i<=tot; i++)
    116         {
    117             add(fail[i],i);
    118         }
    119     }
    120     void dfs(int x,int f)
    121     {
    122         if(End[x])f=1;
    123         dp[x] = f;
    124         for(int i=head[x]; i; i=node[i].next)
    125         {
    126                 dfs(node[i].y,f);
    127         }
    128 
    129     }
    130     void graph()
    131     {
    132         for(int i=0; i<=tot; i++)
    133         {
    134             if(!dp[i])
    135             {
    136                 L++;
    137                 if(!tohash[i])tohash[i] = ++topos;
    138                 for(int j=0; j<26; j++)
    139                 {
    140                     if(!dp[trie[i][j]])
    141                     {
    142                         if(!tohash[trie[i][j]])tohash[trie[i][j]]=++topos;
    143                         maps.m[tohash[i]][tohash[trie[i][j]]]++;
    144                     }
    145                 }
    146             }
    147         }
    148     }
    149 };
    150 
    151 MAP qpow(MAP a,ull b)
    152 {
    153     MAP ans;
    154     ans.init();
    155     MAP base = a;
    156     while(b)
    157     {
    158         if(b&1)ans = ans*base;
    159         base = base * base;
    160         b >>= 1;
    161     }
    162     return ans;
    163 }
    164 
    165 ACTO AC;
    166 
    167 //MAP cal(int l,int r)
    168 //{
    169 //
    170 //    if(l==r)return maps;
    171 //    int mid = r/2;
    172 //    if(r & 1)
    173 //    {
    174 //        return cal(l,mid)+qpow(maps,mid)*cal(l,mid)+qpow(maps,r);
    175 //    }
    176 //    else
    177 //    {
    178 //        return cal(l,mid)+cal(l,mid)*qpow(maps,mid);
    179 //    }
    180 //}
    181 
    182 
    183 
    184 ull qmulti(ull m,ull n)
    185 {
    186     ull ans = 0;
    187     while(n)
    188     {
    189         if(n&1)ans += m;
    190         m += m;
    191         n >>= 1;
    192     }
    193     return ans;
    194 }
    195 ull qpow(ull a,ull b)
    196 {
    197     ull ans = 1;
    198     ull base = a;
    199     while(b)
    200     {
    201         if(b&1)ans = qmulti(ans,base);
    202         base  = qmulti(base,base);
    203         b >>= 1;
    204     }
    205     return ans;
    206 }
    207 
    208 int main()
    209 {
    210     while(~scanf("%d%llu",&n,&len))
    211     {
    212         AC.init();
    213         memset(maps.m,0,sizeof(maps));
    214         char W[10];
    215         for(int i=1; i<=n; i++)
    216         {
    217             scanf(" %s",W);
    218             AC.Insert(W,i);
    219         }
    220         AC.get_fail();
    221         AC.make_edge();
    222         AC.dfs(0,0);
    223         AC.graph();
    224         for(int i=1;i<=2;i++)
    225         {
    226             for(int j=1;j<=2;j++)
    227             {
    228                 printf(" %d ",maps.m[i][j]);
    229             }
    230             puts("");
    231         }
    232         for(int i=1;i<=L+1;i++)maps.m[i][L+1]=1;
    233        MAP ans = qpow(maps,len);
    234        ull total = (qpow(26,len+1)-26)*qpow(25,(1ull<<63)-1);
    235        for(int i=1;i<=L+1;i++)total -= ans.m[1][i];
    236        printf("%llu
    ",total+1);
    237     }
    238 }
    View Code
  • 相关阅读:
    基于vue的可视化编辑器
    IOS系统兼容input keyup事件
    js滚动事件实现滚动触底加载
    移动端 input 输入框实现自带键盘“搜索“功能并修改X
    clipboard.js兼容ios
    js实现点击复制网页内容(基于clipboard.js)
    js实现点击复制网页内容(基于execCommand)
    knn 数字识别
    knn 算法 k个相近邻居
    sklearn 线性回归
  • 原文地址:https://www.cnblogs.com/iwannabe/p/10772324.html
Copyright © 2020-2023  润新知